It looks like Raustats might not be what I am looking for to get SLA level census data quickly. Let’s try Census2016 from Hugh Parsonage.

rr full_2016_census <- Census2016_wide_by_SA2_year %>% filter(year == ‘2016’ ) head(full_2016_census)

Yes - this is what I need.

Loading other tables

rr ancestories_2016 <- Census2016_ancestories %>% filter(year == ‘2016’ ) countries_of_birth_2016 <- Census2016_countries_of_birth %>% filter(year == ‘2016’ ) languages_2016 <- Census2016_languages %>% filter(year == ‘2016’ ) religions_2016 <- Census2016_religions %>% filter(year == ‘2016’ )

Feature Engineering

Each of the four variables ancestory, country of birth, languages and religions are quite granular, and it may make sense to look at these variables at a lower level of granularity.

Ancestory:

rr download.file(‘https://www.abs.gov.au/AUSSTATS/subscriber.nsf/log?openagent&12490do0001_201912.xls&1249.0&Data%20Cubes&674EFC4CA0A3D8FDCA2584D30012B905&0&2019&18.12.2019&Latest’, ‘./Data/ancestry_classification.xls’, method = ‘libcurl’)

ancestry_classification_4dig <- readxl::read_xls(‘./Data/ancestry_classification.xls’, sheet = ‘Table 1.3’, skip = 7, col_names = c(‘X1’, ‘X2’, ‘Ancestory_Code_4’, ‘Ancestory’)) %>% filter(!is.na(Ancestory)) %>% select(Ancestory_Code_4, Ancestory)

ancestry_classification_1dig <- readxl::read_xls(‘./Data/ancestry_classification.xls’, sheet = ‘Table 1.1’, skip = 5, col_names = c(‘Ancestory_Code_1’, ‘Ancestory_Group’))%>% filter(!is.na(Ancestory_Group))

Country of birth

rr download.file(‘https://www.abs.gov.au/ausstats/subscriber.nsf/log?openagent&sacc_12690do0001_201903.xls&1269.0&Data%20Cubes&480BD730AF42D515CA2583BD007707C5&0&2016&15.03.2019&Latest’, ‘./Data/country_classification.xls’, method = ‘libcurl’)

country_classification_4dig <- readxl::read_xls(‘./Data/country_classification.xls’, sheet = ‘Table 1.3’, skip = 7, col_names = c(‘X1’, ‘X2’, ‘Country_Code_4’, ‘Country’)) %>% filter(!is.na(Country)) %>% select(-X1, -X2)

country_classification_2dig <- readxl::read_xls(‘./Data/country_classification.xls’, sheet = ‘Table 1.2’, skip = 6, col_names = c(‘X1’, ‘Country_Code_2’, ‘Country_Name_2’)) %>% filter(!is.na(Country_Name_2)) %>% select(-X1)

country_classification_1dig <- readxl::read_xls(‘./Data/country_classification.xls’, sheet = ‘Table 1.1’, skip = 5, col_names = c(‘Country_Code_1’, ‘Country_Group’)) %>% filter(!is.na(Country_Group))

Language

rr download.file(‘https://www.abs.gov.au/AUSSTATS/subscriber.nsf/log?openagent&ASCL_12670DO0001_201703.xls&1267.0&Data%20Cubes&F84620CF6E13F7E8CA257FF1001E68A7&0&2016&28.03.2017&Latest’, ‘./Data/language_classification.xls’, method = ‘libcurl’)

language_classification_4dig <- readxl::read_xls(‘./Data/language_classification.xls’, sheet = ‘Table 1.3’, skip = 8, col_names = c(‘X1’, ‘X2’, ‘X3’, ‘X4’, ‘Language_Code_3’, ‘Language’)) %>% filter(!is.na(Language)) %>% select(-X1, -X2, -X3, -X4)

language_classification_1dig <- readxl::read_xls(‘./Data/language_classification.xls’, sheet = ‘Table 1.1’, skip = 5, col_names = c(‘Language_Code_1’, ‘Language_Group’)) %>% filter(!is.na(Language_Group))

Religion

rr download.file(‘https://www.abs.gov.au/AUSSTATS/subscriber.nsf/log?openagent&ASCRG_12660DO0001_201707.xls&1266.0&Data%20Cubes&B3EAFE3FE6180D37CA257FF1001E673C&0&2016&14.07.2017&Latest’, ‘./Data/religion_classification.xls’, method = ‘libcurl’)

religion_classification_3dig <- readxl::read_xls(‘./Data/religion_classification.xls’, sheet = ‘Table 1.2’, skip = 6, col_names = c(‘X1’, ‘Religion_Code_3’, ‘Religion’)) %>% filter(!is.na(Religion)) %>% select(-X1)

religion_classification_1dig <- readxl::read_xls(‘./Data/religion_classification.xls’, sheet = ‘Table 1.1’, skip = 5, col_names = c(‘Religion_Code_1’, ‘Religion_Group’)) %>% filter(!is.na(Religion_Group))

Combine with election data

Aggregate at the SA2 level - add unless variable contains median, average, persons_per_bedroom

census_2016_all_vars <- Census2016_wide_by_SA2_year %>% 
  filter(year == '2016') %>% 
  rowwise() %>% 
  mutate(sa2_id = paste0(substr(sa2_code, 1, 1), substr(sa2_code, 6, 9))) %>% 
  filter(isMissing == FALSE) %>% 
  mutate(percent_female = female/persons,
         percent_defacto = defacto_persons/persons,
         percent_married = married_persons/persons,
         percent_indig = indig_persons/persons,
         percent_born_in_australia = born_in_australia/persons,
         percent_unit = flat_or_unit/n_dwellings,
         percent_mortgage = dwelling_owned_mortgage/n_dwellings,
         percent_rent = dwelling_rented/n_dwellings)


census_2016_means <- census_2016_all_vars %>% 
  select(median_age, median_household_income, average_household_size, 
         persons_per_bedroom, median_weekly_rent, median_annual_mortgage, sa2_id) %>% 
  group_by(sa2_id) %>% 
  summarise_all(mean, na.rm = TRUE)

census_2016_counts <- census_2016_all_vars %>% 
  select(n_dwellings, persons, female, male, 
         married_persons, married_females, married_males, defacto_persons, 
         defacto_females, defacto_males, notmarried_persons, 
         notmarried_females, notmarried_males, indig_persons, 
         indig_males, indig_females, non_indig_persons, 
         non_indig_females, non_indig_males, not_stated_indig_persons, 
         not_stated_indig_males, not_stated_indig_females, 
         born_in_australia, born_overseas, country_not_stated, 
         separate_house, flat_or_unit, housing_other_or_not_stated, semi_or_townhouse, 
         dwelling_owned_outright, dwelling_owned_mortgage, dwelling_other_or_not_stated,
         dwelling_rented, sa2_id) %>% 
  group_by(sa2_id) %>% 
  summarise_all(sum, na.rm = TRUE) %>% 
  mutate(percent_female = female/persons,
         percent_defacto = defacto_persons/persons,
         percent_married = married_persons/persons,
         percent_indig = indig_persons/persons,
         percent_born_in_australia = born_in_australia/persons,
         percent_unit = flat_or_unit/n_dwellings,
         percent_mortgage = dwelling_owned_mortgage/n_dwellings,
         percent_rent = dwelling_rented/n_dwellings)

So what I need is weighted demographic data for each of the polling places based on the number of people from each SLA2 who voted at the polling place. Since we don’t know who voted where, and who can vote at all, we are making the naive assumptions that * Voters at each SLA are similar * Voters are representitive of census respondents at the SLA2 level.

Download and load polling places by SA1


download.file('https://www.aec.gov.au/Elections/Federal_Elections/2016/files/polling-place-by-sa1s-2016.xlsx', './Data/polling-place-by-sa1s-2016.xlsx', method = 'libcurl')
trying URL 'https://www.aec.gov.au/Elections/Federal_Elections/2016/files/polling-place-by-sa1s-2016.xlsx'
Content type 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' length 31087463 bytes (29.6 MB)
==================================================
downloaded 29.6 MB
polling_place_data <- readxl::read_xlsx('./Data/polling-place-by-sa1s-2016.xlsx')

Aggregate polling place data to SA2

rr polling_place_sa2 <- polling_place_data %>% mutate(sa2_id = floor(SA1_id / 100)) %>% group_by(year, state_ab, div_nm, pp_id, pp_nm, sa2_id) %>% summarise(votes = sum(votes))

Combine with demographic data and aggregate

polling_place_demog <- polling_place_sa2 %>% 
  mutate(sa2_id = as.character(sa2_id)) %>% 
  inner_join(census_2016_all_vars)

polling_place_demog_means <- polling_place_demog %>% 
  select(year, state_ab, div_nm, pp_id, pp_nm, sa2_id, votes,
         median_age, median_household_income, average_household_size, 
         persons_per_bedroom, median_weekly_rent, median_annual_mortgage,
         percent_female, percent_defacto, percent_married, percent_indig, 
         percent_born_in_australia, percent_unit, percent_mortgage, percent_rent) %>% 
  group_by(year, state_ab, div_nm, pp_id, pp_nm) %>% 
  summarise_at(vars(median_age, median_household_income, 
                    average_household_size, persons_per_bedroom, median_weekly_rent, 
                    median_annual_mortgage, percent_female, percent_defacto, 
                    percent_married, percent_indig, percent_born_in_australia,
                    percent_unit, percent_mortgage, 
                    percent_rent), funs(weighted.mean(., w=votes)))

Add in 2pp at the polling booth level

rr election_2pp <- twoparty_pollingbooth_download()

trying URL 'https://github.com/ropenscilabs/eechidna/raw/master/extra-data/tpp_pp.rda'
Content type 'application/octet-stream' length 1677810 bytes (1.6 MB)
==================================================
downloaded 1.6 MB

rr polling_place_2pp <- polling_place_demog_means %>% group_by() %>% rename(StateAb = state_ab, DivisionNm = div_nm, PollingPlace = pp_nm) %>% mutate(DivisionNm = toupper(DivisionNm), PollingPlace = toupper(PollingPlace)) %>% left_join(election_2pp %>% filter(year == 2016))

Joining, by = c(\year\, \StateAb\, \DivisionNm\, \PollingPlace\)

Check for missing data

polling_place_2pp %>% 
  summarise_all(funs(sum(is.na(.))))

Which booths are null?

polling_place_2pp %>% 
  filter(is.na(TotalVotes)) %>% 
  tabyl(PollingPlace)
 PollingPlace   n percent
       ABSENT 150    0.25
       POSTAL 150    0.25
     PRE-POLL 150    0.25
  PROVISIONAL 150    0.25

So the Absent, Postals, Pre-Poll and Provisional votes aren’t in this table. Let’s come back to those…

polling_place_2pp %>% 
  summarise_all(funs(sum(is.null(.))))

Remove the rows with NAs

polling_place_2pp_clean <- polling_place_2pp %>% 
  filter(!is.na(TotalVotes))
polling_place_2pp_clean %>% 
  summarise_all(funs(sum(is.na(.))))

Which polling stations have missing data? Not too concerned about post code, as there are some special booths

polling_place_2pp_clean %>% 
  filter(is.na(median_age)|is.na(Latitude))

Looks like mobile teams and prepoll centres, and only latitude and longitude. Will remove the Brand mobile team, as the demographic data does not look valid.

polling_place_2pp_clean<- polling_place_2pp_clean %>% 
  dplyr::filter(PollingPlaceID != 65161)

Visualising Basic Statistics

polling_place_2pp_clean %>% 
  ggplot(aes(x = LNP_Percent/100)) + stat_density(geom="line", colour = 'blue') +
  theme_classic(base_size = 16) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: LNP 2 Party Preferred Percentage', x = '2PP Percentage', 
       y = 'Density', subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(x = ALP_Percent/100)) + stat_density(geom="line", colour = 'red') +
  theme_classic(base_size = 16) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', x = '2PP Percentage', 
       y = 'Density', subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(x = Swing/100)) + stat_density(geom="line", colour = 'purple') +
  theme_classic(base_size = 16) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: Swing to Incumbent', x = 'Swing', 
       y = 'Density', subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(x = median_household_income)) + stat_density(geom="line", colour = 'purple') +
  theme_classic(base_size = 16) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Census: Median Income', x = 'Median Income', 
       y = 'Density', subtitle = 'by Polling Booth, Unweighted')

State Breakdowns

Can we look at these distributions by state?

polling_place_2pp_clean %>% 
  ggplot(aes(x = ALP_Percent/100, colour = StateAb)) + 
  stat_density(geom="line", position = 'dodge') +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', x = '2PP Percentage', 
       y = 'Frequency', subtitle = 'by Polling Booth, Unweighted')

What about by median income

polling_place_2pp_clean %>%
  ggplot(aes(y = ALP_Percent/100, x = median_household_income, colour = StateAb)) +
  geom_point() +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', x = 'Booth Median Income',
       y = 'ALP 2pp Percentage', subtitle = 'by Polling Booth, Unweighted') +
  facet_wrap(~StateAb, nrow = 4)

What about comparing NSW electorates? There seems to be an odd separation in income bands for low ALP 2pp. Could this be a regional vs city difference?

fp_booth_16 <- firstpref_pollingbooth_download() %>% 
  filter(year == 2016)
trying URL 'https://github.com/ropenscilabs/eechidna/raw/master/extra-data/fp_pp.rda'
Content type 'application/octet-stream' length 3227934 bytes (3.1 MB)
==================================================
downloaded 3.1 MB
polling_2cp <- read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseTcpByCandidateByPollingPlaceDownload-20499.csv', skip = 1)

polling_2pp <- read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseTppByPollingPlaceDownload-20499.csv', skip = 1)

fp_booth_2016 <- read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-NSW.csv', skip = 1) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-VIC.csv', skip = 1)) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-QLD.csv', skip = 1)) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-SA.csv', skip = 1)) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-WA.csv', skip = 1)) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-TAS.csv', skip = 1)) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-NT.csv', skip = 1)) %>% 
  rbind(read_csv('https://results.aec.gov.au/20499/Website/Downloads/HouseStateFirstPrefsByPollingPlaceDownload-20499-ACT.csv', skip = 1))
coalition_contest_2016 <- fp_booth_2016 %>% 
  group_by(DivisionNm, PartyNm, HistoricElected) %>% 
  summarise(OrdinaryVotes = sum(OrdinaryVotes)) %>%
  filter(PartyNm %in% c('Liberal', 'Country Liberals (NT)',
                        'Liberal National Party of Queensland',
                        'The Nationals')) %>% 
  group_by(DivisionNm) %>% 
  top_n(1) %>% 
  select(DivisionNm, PartyNm)
Selecting by OrdinaryVotes

If we look at a couple of the states where high income booths tend to vote strongly for the coalition as well as lower income booths, we can see that some (but not all) of the lower income booths are contested by The Nationals. This indicares (not surprisingly) that Nationals voters and Liberal voters are different socio-economically, or possibly that city and country coalition voters differ.

polling_place_2pp_clean %>% 
  mutate(DivisionNm = stringr::str_to_title(DivisionNm)) %>%
  inner_join(coalition_contest_2016) %>% 
  filter(StateAb == 'NSW') %>% 
  ggplot(aes(y = ALP_Percent/100, x = median_household_income, colour = PartyNm)) +
  geom_point(size = 3) +
  theme_classic(base_size = 16) + scale_color_manual(values = c('blue', 'dark green')) +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', x = 'Booth Median Income',
       y = 'ALP 2pp Percentage', colour = 'Coalition Party', 
       subtitle = 'by Polling Booth, Unweighted (NSW)') 

polling_place_2pp_clean %>% 
  mutate(DivisionNm = stringr::str_to_title(DivisionNm)) %>%
  inner_join(coalition_contest_2016) %>% 
  filter(StateAb == 'VIC') %>% 
  ggplot(aes(y = ALP_Percent/100, x = median_household_income, colour = PartyNm)) +
  geom_point(size = 3) +
  theme_classic(base_size = 16) + scale_color_manual(values = c('blue', 'dark green')) +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', x = 'Booth Median Income',
       y = 'ALP 2pp Percentage', colour = 'Coalition Party', 
       subtitle = 'by Polling Booth, Unweighted (VIC)') 

This effect is less clear in states where the Nationals aren’t as prominent, either because the Nationals aren’t as prominent (SA, WA, TAS), or are merged with the Liberals (QLD).

polling_place_2pp_clean %>% 
  mutate(DivisionNm = stringr::str_to_title(DivisionNm)) %>%
  inner_join(coalition_contest_2016) %>% 
  filter(StateAb == 'WA') %>% 
  ggplot(aes(y = ALP_Percent/100, x = median_household_income, colour = PartyNm)) +
  geom_point(size = 3) +
  theme_classic(base_size = 16) + scale_color_manual(values = c('blue', 'dark green')) +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', x = 'Booth Median Income',
       y = 'ALP 2pp Percentage', colour = 'Coalition Party', 
       subtitle = 'by Polling Booth, Unweighted (WA)') 

Perhaps we would be better off using the geographical classifications from the AEC.

library(rvest)

webpage <- read_html("http://results.aec.gov.au/20499/Website/HouseDivisionClassifications-20499-NAT.htm")

Division_Classifications <- webpage %>%
  html_nodes("#divisionClassifications") %>% 
  html_table(fill = TRUE) %>%
  .[[1]]
Division_Classifications <- Division_Classifications %>% 
  filter(Division != 'Total Enrolment')

polling_place_2pp_clean<- polling_place_2pp_clean %>% 
  mutate(Division = stringr::str_to_title(DivisionNm)) %>%
  inner_join(Division_Classifications)
Joining, by = "Division"

The graph below shows that the booths that have a low ALP 2pp and a low median income are primarily rural booths. This relationship seems stronger than the Lib/Nat split.

polling_place_2pp_clean %>% 
  filter(StateAb == 'NSW') %>% 
  ggplot(aes(y = ALP_Percent/100, x = median_household_income, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', 
       y = 'ALP 2pp Percentage',
       x = 'Booth Median Income', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted (NSW)')

Looking at all states we see a similar relationship, although less strong than in NSW.

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = median_household_income, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::dollar) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Median Booth Income', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

What about some of the other variables?

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = average_household_size, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Average Household Size', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_female, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_x_continuous(labels=scales::percent) +
  scale_y_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent Female', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  select(ALP_Percent, Swing, median_age, median_household_income, average_household_size,
         persons_per_bedroom, median_weekly_rent, median_annual_mortgage,
         percent_female, percent_defacto, percent_born_in_australia,
         percent_unit, percent_mortgage, percent_rent) %>% 
  cor %>% 
  kable()
ALP_Percent Swing median_age median_household_income average_household_size persons_per_bedroom median_weekly_rent median_annual_mortgage percent_female percent_defacto percent_born_in_australia percent_unit percent_mortgage percent_rent
ALP_Percent 1.0000000 -0.2924398 -0.4148188 0.0049899 0.1785014 0.3698085 0.1675435 0.1230552 0.1245266 0.1270139 -0.3301750 NA NA NA
Swing -0.2924398 1.0000000 0.0592447 0.1044271 -0.0872137 0.0165807 0.0817794 0.0964162 0.0734776 0.0042180 -0.0448560 NA NA NA
median_age -0.4148188 0.0592447 1.0000000 -0.4804955 -0.5175758 -0.5943939 -0.4116337 -0.4221270 -0.0574141 -0.0502159 0.5197535 NA NA NA
median_household_income 0.0049899 0.1044271 -0.4804955 1.0000000 0.4183950 0.3362658 0.8029106 0.8644605 0.1982974 -0.0693950 -0.3959055 NA NA NA
average_household_size 0.1785014 -0.0872137 -0.5175758 0.4183950 1.0000000 0.3887206 0.3408666 0.3284517 -0.0032833 -0.5164718 -0.3020229 NA NA NA
persons_per_bedroom 0.3698085 0.0165807 -0.5943939 0.3362658 0.3887206 1.0000000 0.4120754 0.4015767 0.0035249 0.0094906 -0.5938012 NA NA NA
median_weekly_rent 0.1675435 0.0817794 -0.4116337 0.8029106 0.3408666 0.4120754 1.0000000 0.9276979 0.4307971 -0.1337758 -0.6013994 NA NA NA
median_annual_mortgage 0.1230552 0.0964162 -0.4221270 0.8644605 0.3284517 0.4015767 0.9276979 1.0000000 0.3628219 -0.1183373 -0.5499270 NA NA NA
percent_female 0.1245266 0.0734776 -0.0574141 0.1982974 -0.0032833 0.0035249 0.4307971 0.3628219 1.0000000 -0.1010107 -0.1145354 NA NA NA
percent_defacto 0.1270139 0.0042180 -0.0502159 -0.0693950 -0.5164718 0.0094906 -0.1337758 -0.1183373 -0.1010107 1.0000000 0.2383005 NA NA NA
percent_born_in_australia -0.3301750 -0.0448560 0.5197535 -0.3959055 -0.3020229 -0.5938012 -0.6013994 -0.5499270 -0.1145354 0.2383005 1.0000000 NA NA NA
percent_unit NA NA NA NA NA NA NA NA NA NA NA 1 NA NA
percent_mortgage NA NA NA NA NA NA NA NA NA NA NA NA 1 NA
percent_rent NA NA NA NA NA NA NA NA NA NA NA NA NA 1
polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = persons_per_bedroom, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Persons per Bedroom', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_unit, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent of Dwellings - Unit', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_mortgage, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent of Dwellings - Under Mortgage', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_rent, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent of Dwellings - Renting', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_indig, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent Indigeneous', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_born_in_australia, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent Born in Australia', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

polling_place_2pp_clean %>% 
  ggplot(aes(y = ALP_Percent/100, x = percent_defacto, colour = Demographic)) +
  geom_point(size = 1) +
  theme_classic(base_size = 16) + scale_color_brewer(palette = "Dark2") +
  theme(legend.position = 'bottom') +
  scale_y_continuous(labels=scales::percent) +
  scale_x_continuous(labels=scales::percent) +
  labs(title = '2016 Election: ALP 2 Party Preferred Percentage', y = 'ALP 2pp Percentage',
       x = 'Percent in a Defacto Relationship', colour = 'Region',
       subtitle = 'by Polling Booth, Unweighted')

What to do next?

Add extra variables Look at lagged results Build models

Simple Model Building

Linear Models

Can we build a simple linear model to predict 2pp

library(MASS)
library(car)
alp_2pp_lm_demog <- 
  lm(ALP_Percent ~ median_household_income*Demographic + percent_indig + 
       percent_female + percent_defacto + percent_born_in_australia + 
       percent_rent + median_weekly_rent + median_age, 
     data = polling_place_2pp_clean)

summary(alp_2pp_lm_demog)

Call:
lm(formula = ALP_Percent ~ median_household_income * Demographic + 
    percent_indig + percent_female + percent_defacto + percent_born_in_australia + 
    percent_rent + median_weekly_rent + median_age, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.536  -8.043  -0.237   7.809  73.017 

Coefficients:
                                                        Estimate Std. Error t value Pr(>|t|)
(Intercept)                                            1.096e+02  5.585e+00  19.631  < 2e-16
median_household_income                               -5.176e-04  1.767e-05 -29.284  < 2e-16
DemographicOuter Metropolitan                         -1.583e+00  1.866e+00  -0.849  0.39614
DemographicProvincial                                 -1.072e+01  2.381e+00  -4.505 6.75e-06
DemographicRural                                      -3.616e+01  1.977e+00 -18.288  < 2e-16
percent_indig                                          1.650e+01  2.761e+00   5.976 2.39e-09
percent_female                                         1.676e+01  1.135e+01   1.477  0.13971
percent_defacto                                        1.514e+02  7.955e+00  19.027  < 2e-16
percent_born_in_australia                             -9.365e+00  2.329e+00  -4.022 5.83e-05
percent_rent                                          -1.277e+01  2.510e+00  -5.088 3.70e-07
median_weekly_rent                                     3.201e-02  3.118e-03  10.265  < 2e-16
median_age                                            -8.904e-01  4.622e-02 -19.264  < 2e-16
median_household_income:DemographicOuter Metropolitan -3.321e-06  2.131e-05  -0.156  0.87619
median_household_income:DemographicProvincial          8.332e-05  3.051e-05   2.731  0.00634
median_household_income:DemographicRural               3.091e-04  2.498e-05  12.372  < 2e-16
                                                         
(Intercept)                                           ***
median_household_income                               ***
DemographicOuter Metropolitan                            
DemographicProvincial                                 ***
DemographicRural                                      ***
percent_indig                                         ***
percent_female                                           
percent_defacto                                       ***
percent_born_in_australia                             ***
percent_rent                                          ***
median_weekly_rent                                    ***
median_age                                            ***
median_household_income:DemographicOuter Metropolitan    
median_household_income:DemographicProvincial         ** 
median_household_income:DemographicRural              ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.15 on 7775 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.3995,    Adjusted R-squared:  0.3984 
F-statistic: 369.5 on 14 and 7775 DF,  p-value: < 2.2e-16
anova(alp_2pp_lm_demog)
Analysis of Variance Table

Response: ALP_Percent
                                      Df  Sum Sq Mean Sq   F value    Pr(>F)    
median_household_income                1     172     172    1.1620  0.281093    
Demographic                            3  453116  151039 1022.4845 < 2.2e-16 ***
percent_indig                          1   31754   31754  214.9672 < 2.2e-16 ***
percent_female                         1      48      48    0.3245  0.568953    
percent_defacto                        1   74787   74787  506.2809 < 2.2e-16 ***
percent_born_in_australia              1   59450   59450  402.4580 < 2.2e-16 ***
percent_rent                           1    5547    5547   37.5484 9.352e-10 ***
median_weekly_rent                     1    1397    1397    9.4542  0.002114 ** 
median_age                             1  110129  110129  745.5389 < 2.2e-16 ***
median_household_income:Demographic    3   27664    9221   62.4249 < 2.2e-16 ***
Residuals                           7775 1148503     148                        
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(alp_2pp_lm_demog)

vif(alp_2pp_lm_demog)
                                            GVIF Df GVIF^(1/(2*Df))
median_household_income                 7.316624  1        2.704926
Demographic                         18261.455172  3        5.131637
percent_indig                           1.706153  1        1.306198
percent_female                          1.650000  1        1.284523
percent_defacto                         1.614552  1        1.270650
percent_born_in_australia               4.563213  1        2.136168
percent_rent                            3.299426  1        1.816432
median_weekly_rent                      6.269462  1        2.503889
median_age                              3.407630  1        1.845977
median_household_income:Demographic 13002.761451  3        4.849228
# crPlots(alp_2pp_lm_demog)
# ceresPlots(alp_2pp_lm_demog)

library(gvlma)
gv_alp_2pp_lm_demog <- gvlma(alp_2pp_lm_demog)
summary(gv_alp_2pp_lm_demog)

Call:
lm(formula = ALP_Percent ~ median_household_income * Demographic + 
    percent_indig + percent_female + percent_defacto + percent_born_in_australia + 
    percent_rent + median_weekly_rent + median_age, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.536  -8.043  -0.237   7.809  73.017 

Coefficients:
                                                        Estimate Std. Error t value Pr(>|t|)
(Intercept)                                            1.096e+02  5.585e+00  19.631  < 2e-16
median_household_income                               -5.176e-04  1.767e-05 -29.284  < 2e-16
DemographicOuter Metropolitan                         -1.583e+00  1.866e+00  -0.849  0.39614
DemographicProvincial                                 -1.072e+01  2.381e+00  -4.505 6.75e-06
DemographicRural                                      -3.616e+01  1.977e+00 -18.288  < 2e-16
percent_indig                                          1.650e+01  2.761e+00   5.976 2.39e-09
percent_female                                         1.676e+01  1.135e+01   1.477  0.13971
percent_defacto                                        1.514e+02  7.955e+00  19.027  < 2e-16
percent_born_in_australia                             -9.365e+00  2.329e+00  -4.022 5.83e-05
percent_rent                                          -1.277e+01  2.510e+00  -5.088 3.70e-07
median_weekly_rent                                     3.201e-02  3.118e-03  10.265  < 2e-16
median_age                                            -8.904e-01  4.622e-02 -19.264  < 2e-16
median_household_income:DemographicOuter Metropolitan -3.321e-06  2.131e-05  -0.156  0.87619
median_household_income:DemographicProvincial          8.332e-05  3.051e-05   2.731  0.00634
median_household_income:DemographicRural               3.091e-04  2.498e-05  12.372  < 2e-16
                                                         
(Intercept)                                           ***
median_household_income                               ***
DemographicOuter Metropolitan                            
DemographicProvincial                                 ***
DemographicRural                                      ***
percent_indig                                         ***
percent_female                                           
percent_defacto                                       ***
percent_born_in_australia                             ***
percent_rent                                          ***
median_weekly_rent                                    ***
median_age                                            ***
median_household_income:DemographicOuter Metropolitan    
median_household_income:DemographicProvincial         ** 
median_household_income:DemographicRural              ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.15 on 7775 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.3995,    Adjusted R-squared:  0.3984 
F-statistic: 369.5 on 14 and 7775 DF,  p-value: < 2.2e-16


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = alp_2pp_lm_demog) 
library(MASS)
library(car)
alp_2pp_lm_demog <- 
  lm(ALP_Percent ~ median_household_income + Demographic + percent_indig + 
       percent_defacto + percent_born_in_australia + 
       percent_rent + median_weekly_rent + median_age, 
     data = polling_place_2pp_clean)

summary(alp_2pp_lm_demog)

Call:
lm(formula = ALP_Percent ~ median_household_income + Demographic + 
    percent_indig + percent_defacto + percent_born_in_australia + 
    percent_rent + median_weekly_rent + median_age, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.420  -8.135  -0.258   8.075  82.895 

Coefficients:
                                Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    1.235e+02  2.624e+00  47.065  < 2e-16 ***
median_household_income       -4.488e-04  1.369e-05 -32.780  < 2e-16 ***
DemographicOuter Metropolitan -1.567e+00  4.622e-01  -3.391 0.000699 ***
DemographicProvincial         -2.691e+00  6.274e-01  -4.290 1.81e-05 ***
DemographicRural              -1.417e+01  6.081e-01 -23.303  < 2e-16 ***
percent_indig                  1.292e+01  2.769e+00   4.665 3.14e-06 ***
percent_defacto                1.524e+02  7.826e+00  19.480  < 2e-16 ***
percent_born_in_australia     -1.122e+01  2.141e+00  -5.240 1.65e-07 ***
percent_rent                  -1.580e+01  2.440e+00  -6.475 1.01e-10 ***
median_weekly_rent             2.988e-02  2.768e-03  10.796  < 2e-16 ***
median_age                    -1.123e+00  4.175e-02 -26.907  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.3 on 7779 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.3846,    Adjusted R-squared:  0.3838 
F-statistic: 486.2 on 10 and 7779 DF,  p-value: < 2.2e-16
anova(alp_2pp_lm_demog)
Analysis of Variance Table

Response: ALP_Percent
                            Df  Sum Sq Mean Sq  F value    Pr(>F)    
median_household_income      1     172     172   1.1345    0.2869    
Demographic                  3  453116  151039 998.2850 < 2.2e-16 ***
percent_indig                1   31754   31754 209.8795 < 2.2e-16 ***
percent_defacto              1   74823   74823 494.5410 < 2.2e-16 ***
percent_born_in_australia    1   56968   56968 376.5284 < 2.2e-16 ***
percent_rent                 1    6712    6712  44.3618 2.915e-11 ***
median_weekly_rent           1    2532    2532  16.7346 4.342e-05 ***
median_age                   1  109539  109539 723.9951 < 2.2e-16 ***
Residuals                 7779 1176949     151                       
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(alp_2pp_lm_demog)

vif(alp_2pp_lm_demog)
                              GVIF Df GVIF^(1/(2*Df))
median_household_income   4.285242  1        2.070083
Demographic               4.657881  3        1.292304
percent_indig             1.675652  1        1.294470
percent_defacto           1.525730  1        1.235204
percent_born_in_australia 3.765674  1        1.940534
percent_rent              3.043732  1        1.744629
median_weekly_rent        4.823036  1        2.196141
median_age                2.713934  1        1.647402
crPlots(alp_2pp_lm_demog)

ceresPlots(alp_2pp_lm_demog)
Factors skipped in drawing CERES plots.

library(gvlma)
gv_alp_2pp_lm_demog <- gvlma(alp_2pp_lm_demog)
summary(gv_alp_2pp_lm_demog)

Call:
lm(formula = ALP_Percent ~ median_household_income + Demographic + 
    percent_indig + percent_defacto + percent_born_in_australia + 
    percent_rent + median_weekly_rent + median_age, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.420  -8.135  -0.258   8.075  82.895 

Coefficients:
                                Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    1.235e+02  2.624e+00  47.065  < 2e-16 ***
median_household_income       -4.488e-04  1.369e-05 -32.780  < 2e-16 ***
DemographicOuter Metropolitan -1.567e+00  4.622e-01  -3.391 0.000699 ***
DemographicProvincial         -2.691e+00  6.274e-01  -4.290 1.81e-05 ***
DemographicRural              -1.417e+01  6.081e-01 -23.303  < 2e-16 ***
percent_indig                  1.292e+01  2.769e+00   4.665 3.14e-06 ***
percent_defacto                1.524e+02  7.826e+00  19.480  < 2e-16 ***
percent_born_in_australia     -1.122e+01  2.141e+00  -5.240 1.65e-07 ***
percent_rent                  -1.580e+01  2.440e+00  -6.475 1.01e-10 ***
median_weekly_rent             2.988e-02  2.768e-03  10.796  < 2e-16 ***
median_age                    -1.123e+00  4.175e-02 -26.907  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.3 on 7779 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.3846,    Adjusted R-squared:  0.3838 
F-statistic: 486.2 on 10 and 7779 DF,  p-value: < 2.2e-16


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = alp_2pp_lm_demog) 

We know that income is important in the non-rural areas, so it might be worth adding an interaction between rural and non-rural and income.

polling_place_2pp_clean <- polling_place_2pp_clean %>% 
  mutate(NonRural_Demographic = if_else(Demographic == 'Rural', 1, 0),
         ALP_Percent_2013 = ALP_Percent + Swing)

alp_2pp_lm_demog <- 
  lm(ALP_Percent ~ median_household_income + Demographic + percent_indig + 
       percent_defacto + percent_born_in_australia + 
       percent_rent + median_weekly_rent + median_age +
       NonRural_Demographic:median_household_income, 
     data = polling_place_2pp_clean)

summary(alp_2pp_lm_demog)

Call:
lm(formula = ALP_Percent ~ median_household_income + Demographic + 
    percent_indig + percent_defacto + percent_born_in_australia + 
    percent_rent + median_weekly_rent + median_age + NonRural_Demographic:median_household_income, 
    data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.412  -8.045  -0.263   7.812  73.408 

Coefficients:
                                               Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                   1.179e+02  2.627e+00  44.891  < 2e-16 ***
median_household_income                      -5.127e-04  1.434e-05 -35.751  < 2e-16 ***
DemographicOuter Metropolitan                -1.916e+00  4.576e-01  -4.188 2.85e-05 ***
DemographicProvincial                        -4.784e+00  6.394e-01  -7.482 8.11e-14 ***
DemographicRural                             -3.503e+01  1.660e+00 -21.098  < 2e-16 ***
percent_indig                                 1.574e+01  2.746e+00   5.731 1.03e-08 ***
percent_defacto                               1.491e+02  7.741e+00  19.259  < 2e-16 ***
percent_born_in_australia                    -8.312e+00  2.128e+00  -3.907 9.43e-05 ***
percent_rent                                 -1.302e+01  2.421e+00  -5.380 7.65e-08 ***
median_weekly_rent                            3.336e-02  2.749e-03  12.138  < 2e-16 ***
median_age                                   -9.197e-01  4.395e-02 -20.929  < 2e-16 ***
median_household_income:NonRural_Demographic  2.933e-04  2.176e-05  13.477  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.16 on 7778 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.3987,    Adjusted R-squared:  0.3978 
F-statistic: 468.8 on 11 and 7778 DF,  p-value: < 2.2e-16
anova(alp_2pp_lm_demog)
Analysis of Variance Table

Response: ALP_Percent
                                               Df  Sum Sq Mean Sq   F value    Pr(>F)    
median_household_income                         1     172     172    1.1608    0.2813    
Demographic                                     3  453116  151039 1021.4652 < 2.2e-16 ***
percent_indig                                   1   31754   31754  214.7529 < 2.2e-16 ***
percent_defacto                                 1   74823   74823  506.0242 < 2.2e-16 ***
percent_born_in_australia                       1   56968   56968  385.2714 < 2.2e-16 ***
percent_rent                                    1    6712    6712   45.3919 1.728e-11 ***
median_weekly_rent                              1    2532    2532   17.1232 3.540e-05 ***
median_age                                      1  109539  109539  740.8063 < 2.2e-16 ***
median_household_income:NonRural_Demographic    1   26856   26856  181.6285 < 2.2e-16 ***
Residuals                                    7778 1150093     148                        
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(alp_2pp_lm_demog)

vif(alp_2pp_lm_demog)
                                                  GVIF Df GVIF^(1/(2*Df))
median_household_income                       4.812683  1        2.193783
Demographic                                  62.371236  3        1.991425
percent_indig                                 1.685419  1        1.298237
percent_defacto                               1.527321  1        1.235848
percent_born_in_australia                     3.804766  1        1.950581
percent_rent                                  3.065885  1        1.750967
median_weekly_rent                            4.865961  1        2.205892
median_age                                    3.077376  1        1.754245
median_household_income:NonRural_Demographic 23.061591  1        4.802249
# crPlots(alp_2pp_lm_demog)
# ceresPlots(alp_2pp_lm_demog)

gv_alp_2pp_lm_demog <- gvlma(alp_2pp_lm_demog)
summary(gv_alp_2pp_lm_demog)

Call:
lm(formula = ALP_Percent ~ median_household_income + Demographic + 
    percent_indig + percent_defacto + percent_born_in_australia + 
    percent_rent + median_weekly_rent + median_age + NonRural_Demographic:median_household_income, 
    data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.412  -8.045  -0.263   7.812  73.408 

Coefficients:
                                               Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                   1.179e+02  2.627e+00  44.891  < 2e-16 ***
median_household_income                      -5.127e-04  1.434e-05 -35.751  < 2e-16 ***
DemographicOuter Metropolitan                -1.916e+00  4.576e-01  -4.188 2.85e-05 ***
DemographicProvincial                        -4.784e+00  6.394e-01  -7.482 8.11e-14 ***
DemographicRural                             -3.503e+01  1.660e+00 -21.098  < 2e-16 ***
percent_indig                                 1.574e+01  2.746e+00   5.731 1.03e-08 ***
percent_defacto                               1.491e+02  7.741e+00  19.259  < 2e-16 ***
percent_born_in_australia                    -8.312e+00  2.128e+00  -3.907 9.43e-05 ***
percent_rent                                 -1.302e+01  2.421e+00  -5.380 7.65e-08 ***
median_weekly_rent                            3.336e-02  2.749e-03  12.138  < 2e-16 ***
median_age                                   -9.197e-01  4.395e-02 -20.929  < 2e-16 ***
median_household_income:NonRural_Demographic  2.933e-04  2.176e-05  13.477  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 12.16 on 7778 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.3987,    Adjusted R-squared:  0.3978 
F-statistic: 468.8 on 11 and 7778 DF,  p-value: < 2.2e-16


ASSESSMENT OF THE LINEAR MODEL ASSUMPTIONS
USING THE GLOBAL TEST ON 4 DEGREES-OF-FREEDOM:
Level of Significance =  0.05 

Call:
 gvlma(x = alp_2pp_lm_demog) 

Lasso Regression

Using a lasso regression,

library(caret)
library(glmnet)

polling_place_2pp_clean_NA <- na.omit(polling_place_2pp_clean) %>% 
  dplyr::select(ALP_Percent, StateAb, median_age, median_household_income, 
                average_household_size, persons_per_bedroom, median_weekly_rent,
                median_annual_mortgage,  percent_female, percent_defacto, 
                percent_married, percent_indig, percent_born_in_australia, 
                percent_unit, percent_mortgage, percent_rent, Demographic)
# Inspect the data
sample_n(polling_place_2pp_clean_NA, 3)
# Split the data into training and test set
set.seed(123)
training.samples <- polling_place_2pp_clean_NA$ALP_Percent %>% 
  createDataPartition(p = 0.8, list = FALSE)
train.data  <- polling_place_2pp_clean_NA[training.samples, ]
test.data <- polling_place_2pp_clean_NA[-training.samples, ]

# Dumy code categorical predictor variables
x <- model.matrix(ALP_Percent~., train.data)[,-1]
# Convert the outcome (class) to a numerical variable
y <- train.data$ALP_Percent

cv.lasso <- cv.glmnet(x, y, alpha = 1, family = "gaussian")

# Fit the final model on the training data
alp_2pp_lasso_demog <- glmnet(x, y, alpha = 1, family = "gaussian",
                lambda = cv.lasso$lambda.min)
# Display regression coefficients
coef(alp_2pp_lasso_demog)
25 x 1 sparse Matrix of class "dgCMatrix"
                                         s0
(Intercept)                    1.506842e+01
StateAbNSW                    -1.054087e+01
StateAbNT                     -7.804646e+00
StateAbQLD                    -1.630224e+01
StateAbSA                     -1.248631e+01
StateAbTAS                    -7.533343e+00
StateAbVIC                    -1.316506e+01
StateAbWA                     -2.007797e+01
median_age                     3.361810e-01
median_household_income       -2.954546e-04
average_household_size         6.490529e+00
persons_per_bedroom            3.567807e+01
median_weekly_rent             1.391279e-02
median_annual_mortgage         1.118316e-04
percent_female                 3.578588e+01
percent_defacto                1.719937e+02
percent_married               -8.101607e+01
percent_indig                 -4.221851e+01
percent_born_in_australia     -2.354045e+01
percent_unit                  -4.874705e+01
percent_mortgage               4.682609e+01
percent_rent                   4.530053e+01
DemographicOuter Metropolitan -3.125436e+00
DemographicProvincial         -3.269083e+00
DemographicRural              -1.363746e+01
summary(alp_2pp_lasso_demog)
          Length Class     Mode   
a0         1     -none-    numeric
beta      24     dgCMatrix S4     
df         1     -none-    numeric
dim        2     -none-    numeric
lambda     1     -none-    numeric
dev.ratio  1     -none-    numeric
nulldev    1     -none-    numeric
npasses    1     -none-    numeric
jerr       1     -none-    numeric
offset     1     -none-    logical
call       6     -none-    call   
nobs       1     -none-    numeric
# Make predictions on the test data
x.test <- model.matrix(ALP_Percent ~., test.data)[,-1]
predictions <- alp_2pp_lasso_demog %>% predict(newx = x.test)
# Model accuracy
observed <- test.data$ALP_Percent

plot(predictions, observed)

cor(predictions, observed)
        [,1]
s0 0.7230284
cor(predictions, observed)^2
        [,1]
s0 0.5227701

What if we include the 2pp from the last election. It appears that Swing is defined as the swing to the Coalition between 2013 and 2016. So if we add the swing to the 2016 2pp, then we obtain the 2013 2pp.

When using a Linear Model, we can get an R^2 of about 84%

polling_place_2pp_clean <- polling_place_2pp_clean %>% 
  mutate(logit_ALP_Percent = log((ALP_Percent/100)/(1-ALP_Percent/100)),
         logit_ALP_Percent_2013 = log((ALP_Percent_2013/100)/(1-ALP_Percent_2013/100))) %>% 
  filter(!is.nan(logit_ALP_Percent)) %>% 
  filter(!is.na(logit_ALP_Percent)) %>% 
  filter(!is.infinite(logit_ALP_Percent)) %>% 
  filter(!is.nan(logit_ALP_Percent_2013)) %>% 
  filter(!is.na(logit_ALP_Percent_2013)) %>% 
  filter(!is.infinite(logit_ALP_Percent_2013))
NaNs produced
alp_2pp_lm_demog_lag <- 
  lm(logit_ALP_Percent ~ percent_indig + percent_defacto +
       percent_rent + median_weekly_rent + median_age + 
       NonRural_Demographic*median_household_income + logit_ALP_Percent_2013, 
     data = polling_place_2pp_clean)

summary(alp_2pp_lm_demog_lag)

Call:
lm(formula = logit_ALP_Percent ~ percent_indig + percent_defacto + 
    percent_rent + median_weekly_rent + median_age + NonRural_Demographic * 
    median_household_income + logit_ALP_Percent_2013, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.1110 -0.1297 -0.0042  0.1305  3.1633 

Coefficients:
                                               Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                   1.096e+00  5.178e-02  21.164  < 2e-16 ***
percent_indig                                 4.115e-01  6.038e-02   6.816 1.00e-11 ***
percent_defacto                               5.801e-01  1.692e-01   3.428 0.000612 ***
percent_rent                                 -3.884e-01  4.807e-02  -8.080 7.48e-16 ***
median_weekly_rent                            2.500e-04  5.992e-05   4.172 3.05e-05 ***
median_age                                   -1.478e-02  9.831e-04 -15.030  < 2e-16 ***
NonRural_Demographic                         -2.116e-01  3.464e-02  -6.108 1.06e-09 ***
median_household_income                      -5.178e-06  3.219e-07 -16.086  < 2e-16 ***
logit_ALP_Percent_2013                        8.974e-01  6.057e-03 148.158  < 2e-16 ***
NonRural_Demographic:median_household_income  2.094e-06  4.902e-07   4.272 1.96e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2802 on 7733 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.8392,    Adjusted R-squared:  0.839 
F-statistic:  4485 on 9 and 7733 DF,  p-value: < 2.2e-16
anova(alp_2pp_lm_demog_lag)
Analysis of Variance Table

Response: logit_ALP_Percent
                                               Df  Sum Sq Mean Sq    F value    Pr(>F)    
percent_indig                                   1    0.02    0.02     0.2255    0.6349    
percent_defacto                                 1   78.03   78.03   993.6088 < 2.2e-16 ***
percent_rent                                    1  415.03  415.03  5285.0079 < 2.2e-16 ***
median_weekly_rent                              1   28.44   28.44   362.1399 < 2.2e-16 ***
median_age                                      1  225.78  225.78  2875.1283 < 2.2e-16 ***
NonRural_Demographic                            1  290.25  290.25  3696.0787 < 2.2e-16 ***
median_household_income                         1  366.95  366.95  4672.7058 < 2.2e-16 ***
logit_ALP_Percent_2013                          1 1763.84 1763.84 22460.9325 < 2.2e-16 ***
NonRural_Demographic:median_household_income    1    1.43    1.43    18.2460 1.965e-05 ***
Residuals                                    7733  607.27    0.08                         
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(alp_2pp_lm_demog_lag)

vif(alp_2pp_lm_demog_lag)
                               percent_indig                              percent_defacto 
                                    1.502748                                     1.366026 
                                percent_rent                           median_weekly_rent 
                                    2.262058                                     4.328613 
                                  median_age                         NonRural_Demographic 
                                    2.884030                                    27.998884 
                     median_household_income                       logit_ALP_Percent_2013 
                                    4.532365                                     1.598839 
NonRural_Demographic:median_household_income 
                                   21.897932 
# crPlots(alp_2pp_lm_demog_lag)
# ceresPlots(alp_2pp_lm_demog_lag)

gv_alp_2pp_lm_demog_lag <- gvlma(alp_2pp_lm_demog_lag)
summary(alp_2pp_lm_demog_lag)

Call:
lm(formula = logit_ALP_Percent ~ percent_indig + percent_defacto + 
    percent_rent + median_weekly_rent + median_age + NonRural_Demographic * 
    median_household_income + logit_ALP_Percent_2013, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.1110 -0.1297 -0.0042  0.1305  3.1633 

Coefficients:
                                               Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                   1.096e+00  5.178e-02  21.164  < 2e-16 ***
percent_indig                                 4.115e-01  6.038e-02   6.816 1.00e-11 ***
percent_defacto                               5.801e-01  1.692e-01   3.428 0.000612 ***
percent_rent                                 -3.884e-01  4.807e-02  -8.080 7.48e-16 ***
median_weekly_rent                            2.500e-04  5.992e-05   4.172 3.05e-05 ***
median_age                                   -1.478e-02  9.831e-04 -15.030  < 2e-16 ***
NonRural_Demographic                         -2.116e-01  3.464e-02  -6.108 1.06e-09 ***
median_household_income                      -5.178e-06  3.219e-07 -16.086  < 2e-16 ***
logit_ALP_Percent_2013                        8.974e-01  6.057e-03 148.158  < 2e-16 ***
NonRural_Demographic:median_household_income  2.094e-06  4.902e-07   4.272 1.96e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2802 on 7733 degrees of freedom
  (52 observations deleted due to missingness)
Multiple R-squared:  0.8392,    Adjusted R-squared:  0.839 
F-statistic:  4485 on 9 and 7733 DF,  p-value: < 2.2e-16

It would be interesting to see whether 2013 alone is a good predictor. Fitting this model gives an R^2 of 82.7%. This means that the other variables add a bit, but not a huge amount.

alp_2pp_lm_demog_lag <- 
  lm(logit_ALP_Percent ~ logit_ALP_Percent_2013, 
     data = polling_place_2pp_clean)

summary(alp_2pp_lm_demog_lag)

Call:
lm(formula = logit_ALP_Percent ~ logit_ALP_Percent_2013, data = polling_place_2pp_clean)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.2357 -0.1441 -0.0115  0.1335  3.2092 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)            0.127440   0.003472    36.7   <2e-16 ***
logit_ALP_Percent_2013 0.954281   0.004944   193.0   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2902 on 7793 degrees of freedom
Multiple R-squared:  0.827, Adjusted R-squared:  0.827 
F-statistic: 3.725e+04 on 1 and 7793 DF,  p-value: < 2.2e-16
anova(alp_2pp_lm_demog_lag)
Analysis of Variance Table

Response: logit_ALP_Percent
                         Df  Sum Sq Mean Sq F value    Pr(>F)    
logit_ALP_Percent_2013    1 3136.83 3136.83   37249 < 2.2e-16 ***
Residuals              7793  656.27    0.08                      
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
plot(alp_2pp_lm_demog_lag)

There are three outlying values - let’s explore these

polling_place_2pp_clean[c(2079, 7267, 2758),]

These booths have fewer than 40 electors, and fewer than 5 for one of the two parties. They are also ‘non-standard’ booths.

It might be interesting to see which booths deviate from the division mean.

LS0tCnRpdGxlOiAiRXhwbG9yZSBDZW5zdXMyMDE2IFBhY2thZ2UiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkl0IGxvb2tzIGxpa2UgUmF1c3RhdHMgbWlnaHQgbm90IGJlIHdoYXQgSSBhbSBsb29raW5nIGZvciB0byBnZXQgU0xBIGxldmVsIGNlbnN1cyBkYXRhIHF1aWNrbHkuIExldCdzIHRyeSBDZW5zdXMyMDE2IGZyb20gSHVnaCBQYXJzb25hZ2UuCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgZWNobz1GQUxTRSwgZXJyb3I9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KENlbnN1czIwMTYpCmxpYnJhcnkoZWVjaGlkbmEpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoa2FibGVFeHRyYSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UpCiMga25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yPUZBTFNFKQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZT1GQUxTRSkKYGBgCgpgYGB7cn0KZnVsbF8yMDE2X2NlbnN1cyA8LSBDZW5zdXMyMDE2X3dpZGVfYnlfU0EyX3llYXIgJT4lIAogIGZpbHRlcih5ZWFyID09ICcyMDE2JyApCmhlYWQoZnVsbF8yMDE2X2NlbnN1cykKYGBgCgpZZXMgLSB0aGlzIGlzIHdoYXQgSSBuZWVkLgoKTG9hZGluZyBvdGhlciB0YWJsZXMKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KYW5jZXN0b3JpZXNfMjAxNiA8LSBDZW5zdXMyMDE2X2FuY2VzdG9yaWVzICU+JSAKICBmaWx0ZXIoeWVhciA9PSAnMjAxNicgKQpjb3VudHJpZXNfb2ZfYmlydGhfMjAxNiA8LSBDZW5zdXMyMDE2X2NvdW50cmllc19vZl9iaXJ0aCAlPiUgCiAgZmlsdGVyKHllYXIgPT0gJzIwMTYnICkKbGFuZ3VhZ2VzXzIwMTYgPC0gQ2Vuc3VzMjAxNl9sYW5ndWFnZXMgJT4lIAogIGZpbHRlcih5ZWFyID09ICcyMDE2JyApCnJlbGlnaW9uc18yMDE2IDwtIENlbnN1czIwMTZfcmVsaWdpb25zICU+JSAKICBmaWx0ZXIoeWVhciA9PSAnMjAxNicgKQpgYGAKCiMgRmVhdHVyZSBFbmdpbmVlcmluZwoKRWFjaCBvZiB0aGUgZm91ciB2YXJpYWJsZXMgYW5jZXN0b3J5LCBjb3VudHJ5IG9mIGJpcnRoLCBsYW5ndWFnZXMgYW5kIHJlbGlnaW9ucyBhcmUgcXVpdGUgZ3JhbnVsYXIsIGFuZCBpdCBtYXkgbWFrZSBzZW5zZSB0byBsb29rIGF0IHRoZXNlIHZhcmlhYmxlcyBhdCBhIGxvd2VyIGxldmVsIG9mIGdyYW51bGFyaXR5LiAKCkFuY2VzdG9yeToKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpkb3dubG9hZC5maWxlKCdodHRwczovL3d3dy5hYnMuZ292LmF1L0FVU1NUQVRTL3N1YnNjcmliZXIubnNmL2xvZz9vcGVuYWdlbnQmMTI0OTBkbzAwMDFfMjAxOTEyLnhscyYxMjQ5LjAmRGF0YSUyMEN1YmVzJjY3NEVGQzRDQTBBM0Q4RkRDQTI1ODREMzAwMTJCOTA1JjAmMjAxOSYxOC4xMi4yMDE5JkxhdGVzdCcsICcuL0RhdGEvYW5jZXN0cnlfY2xhc3NpZmljYXRpb24ueGxzJywgbWV0aG9kID0gJ2xpYmN1cmwnKQoKYW5jZXN0cnlfY2xhc3NpZmljYXRpb25fNGRpZyA8LSAKICByZWFkeGw6OnJlYWRfeGxzKCcuL0RhdGEvYW5jZXN0cnlfY2xhc3NpZmljYXRpb24ueGxzJywgc2hlZXQgPSAnVGFibGUgMS4zJywgCiAgICAgICAgICAgICAgICAgICBza2lwID0gNywgY29sX25hbWVzID0gYygnWDEnLCAnWDInLCAnQW5jZXN0b3J5X0NvZGVfNCcsICdBbmNlc3RvcnknKSkgJT4lIAogIGZpbHRlcighaXMubmEoQW5jZXN0b3J5KSkgJT4lIAogIHNlbGVjdChBbmNlc3RvcnlfQ29kZV80LCBBbmNlc3RvcnkpCgphbmNlc3RyeV9jbGFzc2lmaWNhdGlvbl8xZGlnIDwtIAogIHJlYWR4bDo6cmVhZF94bHMoJy4vRGF0YS9hbmNlc3RyeV9jbGFzc2lmaWNhdGlvbi54bHMnLCBzaGVldCA9ICdUYWJsZSAxLjEnLCAKICAgICAgICAgICAgICAgICAgIHNraXAgPSA1LCBjb2xfbmFtZXMgPSBjKCdBbmNlc3RvcnlfQ29kZV8xJywgJ0FuY2VzdG9yeV9Hcm91cCcpKSU+JSAKICBmaWx0ZXIoIWlzLm5hKEFuY2VzdG9yeV9Hcm91cCkpCmBgYAoKQ291bnRyeSBvZiBiaXJ0aApgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmRvd25sb2FkLmZpbGUoJ2h0dHBzOi8vd3d3LmFicy5nb3YuYXUvYXVzc3RhdHMvc3Vic2NyaWJlci5uc2YvbG9nP29wZW5hZ2VudCZzYWNjXzEyNjkwZG8wMDAxXzIwMTkwMy54bHMmMTI2OS4wJkRhdGElMjBDdWJlcyY0ODBCRDczMEFGNDJENTE1Q0EyNTgzQkQwMDc3MDdDNSYwJjIwMTYmMTUuMDMuMjAxOSZMYXRlc3QnLCAnLi9EYXRhL2NvdW50cnlfY2xhc3NpZmljYXRpb24ueGxzJywgbWV0aG9kID0gJ2xpYmN1cmwnKQoKY291bnRyeV9jbGFzc2lmaWNhdGlvbl80ZGlnIDwtIAogIHJlYWR4bDo6cmVhZF94bHMoJy4vRGF0YS9jb3VudHJ5X2NsYXNzaWZpY2F0aW9uLnhscycsIHNoZWV0ID0gJ1RhYmxlIDEuMycsIAogICAgICAgICAgICAgICAgICAgc2tpcCA9IDcsIGNvbF9uYW1lcyA9IGMoJ1gxJywgJ1gyJywgJ0NvdW50cnlfQ29kZV80JywgJ0NvdW50cnknKSkgJT4lIAogIGZpbHRlcighaXMubmEoQ291bnRyeSkpICU+JSAKICBzZWxlY3QoLVgxLCAtWDIpCgpjb3VudHJ5X2NsYXNzaWZpY2F0aW9uXzJkaWcgPC0gCiAgcmVhZHhsOjpyZWFkX3hscygnLi9EYXRhL2NvdW50cnlfY2xhc3NpZmljYXRpb24ueGxzJywgc2hlZXQgPSAnVGFibGUgMS4yJywgCiAgICAgICAgICAgICAgICAgICBza2lwID0gNiwgY29sX25hbWVzID0gYygnWDEnLCAnQ291bnRyeV9Db2RlXzInLCAnQ291bnRyeV9OYW1lXzInKSkgJT4lIAogIGZpbHRlcighaXMubmEoQ291bnRyeV9OYW1lXzIpKSAlPiUgCiAgc2VsZWN0KC1YMSkKCmNvdW50cnlfY2xhc3NpZmljYXRpb25fMWRpZyA8LSAKICByZWFkeGw6OnJlYWRfeGxzKCcuL0RhdGEvY291bnRyeV9jbGFzc2lmaWNhdGlvbi54bHMnLCBzaGVldCA9ICdUYWJsZSAxLjEnLCAKICAgICAgICAgICAgICAgICAgIHNraXAgPSA1LCBjb2xfbmFtZXMgPSBjKCdDb3VudHJ5X0NvZGVfMScsICdDb3VudHJ5X0dyb3VwJykpICU+JSAKICBmaWx0ZXIoIWlzLm5hKENvdW50cnlfR3JvdXApKQpgYGAKCkxhbmd1YWdlCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KZG93bmxvYWQuZmlsZSgnaHR0cHM6Ly93d3cuYWJzLmdvdi5hdS9BVVNTVEFUUy9zdWJzY3JpYmVyLm5zZi9sb2c/b3BlbmFnZW50JkFTQ0xfMTI2NzBETzAwMDFfMjAxNzAzLnhscyYxMjY3LjAmRGF0YSUyMEN1YmVzJkY4NDYyMENGNkUxM0Y3RThDQTI1N0ZGMTAwMUU2OEE3JjAmMjAxNiYyOC4wMy4yMDE3JkxhdGVzdCcsICcuL0RhdGEvbGFuZ3VhZ2VfY2xhc3NpZmljYXRpb24ueGxzJywgbWV0aG9kID0gJ2xpYmN1cmwnKQoKbGFuZ3VhZ2VfY2xhc3NpZmljYXRpb25fNGRpZyA8LSAKICByZWFkeGw6OnJlYWRfeGxzKCcuL0RhdGEvbGFuZ3VhZ2VfY2xhc3NpZmljYXRpb24ueGxzJywgc2hlZXQgPSAnVGFibGUgMS4zJywgCiAgICAgICAgICAgICAgICAgICBza2lwID0gOCwgY29sX25hbWVzID0gYygnWDEnLCAnWDInLCAnWDMnLCAnWDQnLCAnTGFuZ3VhZ2VfQ29kZV8zJywgJ0xhbmd1YWdlJykpICU+JSAKICBmaWx0ZXIoIWlzLm5hKExhbmd1YWdlKSkgJT4lIAogIHNlbGVjdCgtWDEsIC1YMiwgLVgzLCAtWDQpCgpsYW5ndWFnZV9jbGFzc2lmaWNhdGlvbl8xZGlnIDwtIAogIHJlYWR4bDo6cmVhZF94bHMoJy4vRGF0YS9sYW5ndWFnZV9jbGFzc2lmaWNhdGlvbi54bHMnLCBzaGVldCA9ICdUYWJsZSAxLjEnLCAKICAgICAgICAgICAgICAgICAgIHNraXAgPSA1LCBjb2xfbmFtZXMgPSBjKCdMYW5ndWFnZV9Db2RlXzEnLCAnTGFuZ3VhZ2VfR3JvdXAnKSkgJT4lIAogIGZpbHRlcighaXMubmEoTGFuZ3VhZ2VfR3JvdXApKQpgYGAKClJlbGlnaW9uCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KZG93bmxvYWQuZmlsZSgnaHR0cHM6Ly93d3cuYWJzLmdvdi5hdS9BVVNTVEFUUy9zdWJzY3JpYmVyLm5zZi9sb2c/b3BlbmFnZW50JkFTQ1JHXzEyNjYwRE8wMDAxXzIwMTcwNy54bHMmMTI2Ni4wJkRhdGElMjBDdWJlcyZCM0VBRkUzRkU2MTgwRDM3Q0EyNTdGRjEwMDFFNjczQyYwJjIwMTYmMTQuMDcuMjAxNyZMYXRlc3QnLCAnLi9EYXRhL3JlbGlnaW9uX2NsYXNzaWZpY2F0aW9uLnhscycsIG1ldGhvZCA9ICdsaWJjdXJsJykKCnJlbGlnaW9uX2NsYXNzaWZpY2F0aW9uXzNkaWcgPC0gCiAgcmVhZHhsOjpyZWFkX3hscygnLi9EYXRhL3JlbGlnaW9uX2NsYXNzaWZpY2F0aW9uLnhscycsIHNoZWV0ID0gJ1RhYmxlIDEuMicsIAogICAgICAgICAgICAgICAgICAgc2tpcCA9IDYsIGNvbF9uYW1lcyA9IGMoJ1gxJywgJ1JlbGlnaW9uX0NvZGVfMycsICdSZWxpZ2lvbicpKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShSZWxpZ2lvbikpICU+JSAKICBzZWxlY3QoLVgxKQoKcmVsaWdpb25fY2xhc3NpZmljYXRpb25fMWRpZyA8LSAKICByZWFkeGw6OnJlYWRfeGxzKCcuL0RhdGEvcmVsaWdpb25fY2xhc3NpZmljYXRpb24ueGxzJywgc2hlZXQgPSAnVGFibGUgMS4xJywgCiAgICAgICAgICAgICAgICAgICBza2lwID0gNSwgY29sX25hbWVzID0gYygnUmVsaWdpb25fQ29kZV8xJywgJ1JlbGlnaW9uX0dyb3VwJykpICU+JSAKICBmaWx0ZXIoIWlzLm5hKFJlbGlnaW9uX0dyb3VwKSkKYGBgCgoKCgojIENvbWJpbmUgd2l0aCBlbGVjdGlvbiBkYXRhCgpBZ2dyZWdhdGUgYXQgdGhlIFNBMiBsZXZlbCAtIGFkZCB1bmxlc3MgdmFyaWFibGUgY29udGFpbnMgbWVkaWFuLCBhdmVyYWdlLCBwZXJzb25zX3Blcl9iZWRyb29tCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KY2Vuc3VzXzIwMTZfYWxsX3ZhcnMgPC0gQ2Vuc3VzMjAxNl93aWRlX2J5X1NBMl95ZWFyICU+JSAKICBmaWx0ZXIoeWVhciA9PSAnMjAxNicpICU+JSAKICByb3d3aXNlKCkgJT4lIAogIG11dGF0ZShzYTJfaWQgPSBwYXN0ZTAoc3Vic3RyKHNhMl9jb2RlLCAxLCAxKSwgc3Vic3RyKHNhMl9jb2RlLCA2LCA5KSkpICU+JSAKICBmaWx0ZXIoaXNNaXNzaW5nID09IEZBTFNFKSAlPiUgCiAgbXV0YXRlKHBlcmNlbnRfZmVtYWxlID0gZmVtYWxlL3BlcnNvbnMsCiAgICAgICAgIHBlcmNlbnRfZGVmYWN0byA9IGRlZmFjdG9fcGVyc29ucy9wZXJzb25zLAogICAgICAgICBwZXJjZW50X21hcnJpZWQgPSBtYXJyaWVkX3BlcnNvbnMvcGVyc29ucywKICAgICAgICAgcGVyY2VudF9pbmRpZyA9IGluZGlnX3BlcnNvbnMvcGVyc29ucywKICAgICAgICAgcGVyY2VudF9ib3JuX2luX2F1c3RyYWxpYSA9IGJvcm5faW5fYXVzdHJhbGlhL3BlcnNvbnMsCiAgICAgICAgIHBlcmNlbnRfdW5pdCA9IGZsYXRfb3JfdW5pdC9uX2R3ZWxsaW5ncywKICAgICAgICAgcGVyY2VudF9tb3J0Z2FnZSA9IGR3ZWxsaW5nX293bmVkX21vcnRnYWdlL25fZHdlbGxpbmdzLAogICAgICAgICBwZXJjZW50X3JlbnQgPSBkd2VsbGluZ19yZW50ZWQvbl9kd2VsbGluZ3MpCgoKY2Vuc3VzXzIwMTZfbWVhbnMgPC0gY2Vuc3VzXzIwMTZfYWxsX3ZhcnMgJT4lIAogIHNlbGVjdChtZWRpYW5fYWdlLCBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgYXZlcmFnZV9ob3VzZWhvbGRfc2l6ZSwgCiAgICAgICAgIHBlcnNvbnNfcGVyX2JlZHJvb20sIG1lZGlhbl93ZWVrbHlfcmVudCwgbWVkaWFuX2FubnVhbF9tb3J0Z2FnZSwgc2EyX2lkKSAlPiUgCiAgZ3JvdXBfYnkoc2EyX2lkKSAlPiUgCiAgc3VtbWFyaXNlX2FsbChtZWFuLCBuYS5ybSA9IFRSVUUpCgpjZW5zdXNfMjAxNl9jb3VudHMgPC0gY2Vuc3VzXzIwMTZfYWxsX3ZhcnMgJT4lIAogIHNlbGVjdChuX2R3ZWxsaW5ncywgcGVyc29ucywgZmVtYWxlLCBtYWxlLCAKICAgICAgICAgbWFycmllZF9wZXJzb25zLCBtYXJyaWVkX2ZlbWFsZXMsIG1hcnJpZWRfbWFsZXMsIGRlZmFjdG9fcGVyc29ucywgCiAgICAgICAgIGRlZmFjdG9fZmVtYWxlcywgZGVmYWN0b19tYWxlcywgbm90bWFycmllZF9wZXJzb25zLCAKICAgICAgICAgbm90bWFycmllZF9mZW1hbGVzLCBub3RtYXJyaWVkX21hbGVzLCBpbmRpZ19wZXJzb25zLCAKICAgICAgICAgaW5kaWdfbWFsZXMsIGluZGlnX2ZlbWFsZXMsIG5vbl9pbmRpZ19wZXJzb25zLCAKICAgICAgICAgbm9uX2luZGlnX2ZlbWFsZXMsIG5vbl9pbmRpZ19tYWxlcywgbm90X3N0YXRlZF9pbmRpZ19wZXJzb25zLCAKICAgICAgICAgbm90X3N0YXRlZF9pbmRpZ19tYWxlcywgbm90X3N0YXRlZF9pbmRpZ19mZW1hbGVzLCAKICAgICAgICAgYm9ybl9pbl9hdXN0cmFsaWEsIGJvcm5fb3ZlcnNlYXMsIGNvdW50cnlfbm90X3N0YXRlZCwgCiAgICAgICAgIHNlcGFyYXRlX2hvdXNlLCBmbGF0X29yX3VuaXQsIGhvdXNpbmdfb3RoZXJfb3Jfbm90X3N0YXRlZCwgc2VtaV9vcl90b3duaG91c2UsIAogICAgICAgICBkd2VsbGluZ19vd25lZF9vdXRyaWdodCwgZHdlbGxpbmdfb3duZWRfbW9ydGdhZ2UsIGR3ZWxsaW5nX290aGVyX29yX25vdF9zdGF0ZWQsCiAgICAgICAgIGR3ZWxsaW5nX3JlbnRlZCwgc2EyX2lkKSAlPiUgCiAgZ3JvdXBfYnkoc2EyX2lkKSAlPiUgCiAgc3VtbWFyaXNlX2FsbChzdW0sIG5hLnJtID0gVFJVRSkgJT4lIAogIG11dGF0ZShwZXJjZW50X2ZlbWFsZSA9IGZlbWFsZS9wZXJzb25zLAogICAgICAgICBwZXJjZW50X2RlZmFjdG8gPSBkZWZhY3RvX3BlcnNvbnMvcGVyc29ucywKICAgICAgICAgcGVyY2VudF9tYXJyaWVkID0gbWFycmllZF9wZXJzb25zL3BlcnNvbnMsCiAgICAgICAgIHBlcmNlbnRfaW5kaWcgPSBpbmRpZ19wZXJzb25zL3BlcnNvbnMsCiAgICAgICAgIHBlcmNlbnRfYm9ybl9pbl9hdXN0cmFsaWEgPSBib3JuX2luX2F1c3RyYWxpYS9wZXJzb25zLAogICAgICAgICBwZXJjZW50X3VuaXQgPSBmbGF0X29yX3VuaXQvbl9kd2VsbGluZ3MsCiAgICAgICAgIHBlcmNlbnRfbW9ydGdhZ2UgPSBkd2VsbGluZ19vd25lZF9tb3J0Z2FnZS9uX2R3ZWxsaW5ncywKICAgICAgICAgcGVyY2VudF9yZW50ID0gZHdlbGxpbmdfcmVudGVkL25fZHdlbGxpbmdzKQpgYGAKCgpTbyB3aGF0IEkgbmVlZCBpcyB3ZWlnaHRlZCBkZW1vZ3JhcGhpYyBkYXRhIGZvciBlYWNoIG9mIHRoZSBwb2xsaW5nIHBsYWNlcyBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIHBlb3BsZSBmcm9tIGVhY2ggU0xBMiB3aG8gdm90ZWQgYXQgdGhlIHBvbGxpbmcgcGxhY2UuIFNpbmNlIHdlIGRvbid0IGtub3cgd2hvIHZvdGVkIHdoZXJlLCBhbmQgd2hvIGNhbiB2b3RlIGF0IGFsbCwgd2UgYXJlIG1ha2luZyB0aGUgbmFpdmUgYXNzdW1wdGlvbnMgdGhhdCAKKiBWb3RlcnMgYXQgZWFjaCBTTEEgYXJlIHNpbWlsYXIKKiBWb3RlcnMgYXJlIHJlcHJlc2VudGl0aXZlIG9mIGNlbnN1cyByZXNwb25kZW50cyBhdCB0aGUgU0xBMiBsZXZlbC4KCkRvd25sb2FkIGFuZCBsb2FkIHBvbGxpbmcgcGxhY2VzIGJ5IFNBMQpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9Cgpkb3dubG9hZC5maWxlKCdodHRwczovL3d3dy5hZWMuZ292LmF1L0VsZWN0aW9ucy9GZWRlcmFsX0VsZWN0aW9ucy8yMDE2L2ZpbGVzL3BvbGxpbmctcGxhY2UtYnktc2Excy0yMDE2Lnhsc3gnLCAnLi9EYXRhL3BvbGxpbmctcGxhY2UtYnktc2Excy0yMDE2Lnhsc3gnLCBtZXRob2QgPSAnbGliY3VybCcpCgpwb2xsaW5nX3BsYWNlX2RhdGEgPC0gcmVhZHhsOjpyZWFkX3hsc3goJy4vRGF0YS9wb2xsaW5nLXBsYWNlLWJ5LXNhMXMtMjAxNi54bHN4JykKYGBgCgoKQWdncmVnYXRlIHBvbGxpbmcgcGxhY2UgZGF0YSB0byBTQTIKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV9zYTIgPC0gcG9sbGluZ19wbGFjZV9kYXRhICU+JSAKICBtdXRhdGUoc2EyX2lkID0gZmxvb3IoU0ExX2lkIC8gMTAwKSkgJT4lIAogIGdyb3VwX2J5KHllYXIsIHN0YXRlX2FiLCBkaXZfbm0sIHBwX2lkLCBwcF9ubSwgc2EyX2lkKSAlPiUgCiAgc3VtbWFyaXNlKHZvdGVzID0gc3VtKHZvdGVzKSkKCmBgYAoKQ29tYmluZSB3aXRoIGRlbW9ncmFwaGljIGRhdGEgYW5kIGFnZ3JlZ2F0ZQoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlX2RlbW9nIDwtIHBvbGxpbmdfcGxhY2Vfc2EyICU+JSAKICBtdXRhdGUoc2EyX2lkID0gYXMuY2hhcmFjdGVyKHNhMl9pZCkpICU+JSAKICBpbm5lcl9qb2luKGNlbnN1c18yMDE2X2FsbF92YXJzKQoKcG9sbGluZ19wbGFjZV9kZW1vZ19tZWFucyA8LSBwb2xsaW5nX3BsYWNlX2RlbW9nICU+JSAKICBzZWxlY3QoeWVhciwgc3RhdGVfYWIsIGRpdl9ubSwgcHBfaWQsIHBwX25tLCBzYTJfaWQsIHZvdGVzLAogICAgICAgICBtZWRpYW5fYWdlLCBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgYXZlcmFnZV9ob3VzZWhvbGRfc2l6ZSwgCiAgICAgICAgIHBlcnNvbnNfcGVyX2JlZHJvb20sIG1lZGlhbl93ZWVrbHlfcmVudCwgbWVkaWFuX2FubnVhbF9tb3J0Z2FnZSwKICAgICAgICAgcGVyY2VudF9mZW1hbGUsIHBlcmNlbnRfZGVmYWN0bywgcGVyY2VudF9tYXJyaWVkLCBwZXJjZW50X2luZGlnLCAKICAgICAgICAgcGVyY2VudF9ib3JuX2luX2F1c3RyYWxpYSwgcGVyY2VudF91bml0LCBwZXJjZW50X21vcnRnYWdlLCBwZXJjZW50X3JlbnQpICU+JSAKICBncm91cF9ieSh5ZWFyLCBzdGF0ZV9hYiwgZGl2X25tLCBwcF9pZCwgcHBfbm0pICU+JSAKICBzdW1tYXJpc2VfYXQodmFycyhtZWRpYW5fYWdlLCBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgCiAgICAgICAgICAgICAgICAgICAgYXZlcmFnZV9ob3VzZWhvbGRfc2l6ZSwgcGVyc29uc19wZXJfYmVkcm9vbSwgbWVkaWFuX3dlZWtseV9yZW50LCAKICAgICAgICAgICAgICAgICAgICBtZWRpYW5fYW5udWFsX21vcnRnYWdlLCBwZXJjZW50X2ZlbWFsZSwgcGVyY2VudF9kZWZhY3RvLCAKICAgICAgICAgICAgICAgICAgICBwZXJjZW50X21hcnJpZWQsIHBlcmNlbnRfaW5kaWcsIHBlcmNlbnRfYm9ybl9pbl9hdXN0cmFsaWEsCiAgICAgICAgICAgICAgICAgICAgcGVyY2VudF91bml0LCBwZXJjZW50X21vcnRnYWdlLCAKICAgICAgICAgICAgICAgICAgICBwZXJjZW50X3JlbnQpLCBmdW5zKHdlaWdodGVkLm1lYW4oLiwgdz12b3RlcykpKQpgYGAKQWRkIGluIDJwcCBhdCB0aGUgcG9sbGluZyBib290aCBsZXZlbApgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmVsZWN0aW9uXzJwcCA8LSB0d29wYXJ0eV9wb2xsaW5nYm9vdGhfZG93bmxvYWQoKSAKCmBgYApgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwIDwtIHBvbGxpbmdfcGxhY2VfZGVtb2dfbWVhbnMgJT4lIAogIGdyb3VwX2J5KCkgJT4lIAogIHJlbmFtZShTdGF0ZUFiID0gc3RhdGVfYWIsCiAgICAgICAgIERpdmlzaW9uTm0gPSBkaXZfbm0sCiAgICAgICAgIFBvbGxpbmdQbGFjZSA9IHBwX25tLAogICAgICAgICBQb2xsaW5nUGxhY2VJRCA9IHBwX2lkKSAlPiUgCiAgbXV0YXRlKERpdmlzaW9uTm0gPSB0b3VwcGVyKERpdmlzaW9uTm0pLAogICAgICAgICBQb2xsaW5nUGxhY2UgPSB0b3VwcGVyKFBvbGxpbmdQbGFjZSkpICU+JSAKICBsZWZ0X2pvaW4oZWxlY3Rpb25fMnBwICU+JSAKICAgICAgICAgICAgICBmaWx0ZXIoeWVhciA9PSAyMDE2KSkKYGBgCgpDaGVjayBmb3IgbWlzc2luZyBkYXRhCgpgYGB7cn0KcG9sbGluZ19wbGFjZV8ycHAgJT4lIAogIHN1bW1hcmlzZV9hbGwoZnVucyhzdW0oaXMubmEoLikpKSkKYGBgCldoaWNoIGJvb3RocyBhcmUgbnVsbD8KCmBgYHtyfQpwb2xsaW5nX3BsYWNlXzJwcCAlPiUgCiAgZmlsdGVyKGlzLm5hKFRvdGFsVm90ZXMpKSAlPiUgCiAgdGFieWwoUG9sbGluZ1BsYWNlKQpgYGAKClNvIHRoZSBBYnNlbnQsIFBvc3RhbHMsIFByZS1Qb2xsIGFuZCBQcm92aXNpb25hbCB2b3RlcyBhcmVuJ3QgaW4gdGhpcyB0YWJsZS4gTGV0J3MgY29tZSBiYWNrIHRvIHRob3NlLi4uIAoKYGBge3J9CnBvbGxpbmdfcGxhY2VfMnBwICU+JSAKICBzdW1tYXJpc2VfYWxsKGZ1bnMoc3VtKGlzLm51bGwoLikpKSkKYGBgCgpSZW1vdmUgdGhlIHJvd3Mgd2l0aCBOQXMKCmBgYHtyfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiA8LSBwb2xsaW5nX3BsYWNlXzJwcCAlPiUgCiAgZmlsdGVyKCFpcy5uYShUb3RhbFZvdGVzKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBzdW1tYXJpc2VfYWxsKGZ1bnMoc3VtKGlzLm5hKC4pKSkpCmBgYAoKV2hpY2ggcG9sbGluZyBzdGF0aW9ucyBoYXZlIG1pc3NpbmcgZGF0YT8gTm90IHRvbyBjb25jZXJuZWQgYWJvdXQgcG9zdCBjb2RlLCBhcyB0aGVyZSBhcmUgc29tZSBzcGVjaWFsIGJvb3RocwoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZmlsdGVyKGlzLm5hKG1lZGlhbl9hZ2UpfGlzLm5hKExhdGl0dWRlKSkKYGBgCkxvb2tzIGxpa2UgbW9iaWxlIHRlYW1zIGFuZCBwcmVwb2xsIGNlbnRyZXMsIGFuZCBvbmx5IGxhdGl0dWRlIGFuZCBsb25naXR1ZGUuIFdpbGwgcmVtb3ZlIHRoZSBCcmFuZCBtb2JpbGUgdGVhbSwgYXMgdGhlIGRlbW9ncmFwaGljIGRhdGEgZG9lcyBub3QgbG9vayB2YWxpZC4KCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW48LSBwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZHBseXI6OmZpbHRlcihQb2xsaW5nUGxhY2VJRCAhPSA2NTE2MSkKYGBgCgoKCiMgVmlzdWFsaXNpbmcgQmFzaWMgU3RhdGlzdGljcwpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBMTlBfUGVyY2VudC8xMDApKSArIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIiwgY29sb3VyID0gJ2JsdWUnKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgbGFicyh0aXRsZSA9ICcyMDE2IEVsZWN0aW9uOiBMTlAgMiBQYXJ0eSBQcmVmZXJyZWQgUGVyY2VudGFnZScsIHggPSAnMlBQIFBlcmNlbnRhZ2UnLCAKICAgICAgIHkgPSAnRGVuc2l0eScsIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeCA9IEFMUF9QZXJjZW50LzEwMCkpICsgc3RhdF9kZW5zaXR5KGdlb209ImxpbmUiLCBjb2xvdXIgPSAncmVkJykgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB4ID0gJzJQUCBQZXJjZW50YWdlJywgCiAgICAgICB5ID0gJ0RlbnNpdHknLCBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkJykKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBTd2luZy8xMDApKSArIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIiwgY29sb3VyID0gJ3B1cnBsZScpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IFN3aW5nIHRvIEluY3VtYmVudCcsIHggPSAnU3dpbmcnLCAKICAgICAgIHkgPSAnRGVuc2l0eScsIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeCA9IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lKSkgKyBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIsIGNvbG91ciA9ICdwdXJwbGUnKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpkb2xsYXIpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgQ2Vuc3VzOiBNZWRpYW4gSW5jb21lJywgeCA9ICdNZWRpYW4gSW5jb21lJywgCiAgICAgICB5ID0gJ0RlbnNpdHknLCBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkJykKYGBgCgojIyBTdGF0ZSBCcmVha2Rvd25zCgpDYW4gd2UgbG9vayBhdCB0aGVzZSBkaXN0cmlidXRpb25zIGJ5IHN0YXRlPwoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gQUxQX1BlcmNlbnQvMTAwLCBjb2xvdXIgPSBTdGF0ZUFiKSkgKyAKICBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIsIHBvc2l0aW9uID0gJ2RvZGdlJykgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IEFMUCAyIFBhcnR5IFByZWZlcnJlZCBQZXJjZW50YWdlJywgeCA9ICcyUFAgUGVyY2VudGFnZScsIAogICAgICAgeSA9ICdGcmVxdWVuY3knLCBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkJykKYGBgCgpXaGF0IGFib3V0IGJ5IG1lZGlhbiBpbmNvbWUKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lCiAgZ2dwbG90KGFlcyh5ID0gQUxQX1BlcmNlbnQvMTAwLCB4ID0gbWVkaWFuX2hvdXNlaG9sZF9pbmNvbWUsIGNvbG91ciA9IFN0YXRlQWIpKSArCiAgZ2VvbV9wb2ludCgpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmRvbGxhcikgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB4ID0gJ0Jvb3RoIE1lZGlhbiBJbmNvbWUnLAogICAgICAgeSA9ICdBTFAgMnBwIFBlcmNlbnRhZ2UnLCBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkJykgKwogIGZhY2V0X3dyYXAoflN0YXRlQWIsIG5yb3cgPSA0KQpgYGAKCldoYXQgYWJvdXQgY29tcGFyaW5nIE5TVyBlbGVjdG9yYXRlcz8gVGhlcmUgc2VlbXMgdG8gYmUgYW4gb2RkIHNlcGFyYXRpb24gaW4gaW5jb21lIGJhbmRzIGZvciBsb3cgQUxQIDJwcC4gQ291bGQgdGhpcyBiZSBhIHJlZ2lvbmFsIHZzIGNpdHkgZGlmZmVyZW5jZT8gCgpgYGB7ciwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmZwX2Jvb3RoXzE2IDwtIGZpcnN0cHJlZl9wb2xsaW5nYm9vdGhfZG93bmxvYWQoKSAlPiUgCiAgZmlsdGVyKHllYXIgPT0gMjAxNikKYGBgCgoKYGBge3IsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nXzJjcCA8LSByZWFkX2NzdignaHR0cHM6Ly9yZXN1bHRzLmFlYy5nb3YuYXUvMjA0OTkvV2Vic2l0ZS9Eb3dubG9hZHMvSG91c2VUY3BCeUNhbmRpZGF0ZUJ5UG9sbGluZ1BsYWNlRG93bmxvYWQtMjA0OTkuY3N2Jywgc2tpcCA9IDEpCgpwb2xsaW5nXzJwcCA8LSByZWFkX2NzdignaHR0cHM6Ly9yZXN1bHRzLmFlYy5nb3YuYXUvMjA0OTkvV2Vic2l0ZS9Eb3dubG9hZHMvSG91c2VUcHBCeVBvbGxpbmdQbGFjZURvd25sb2FkLTIwNDk5LmNzdicsIHNraXAgPSAxKQoKZnBfYm9vdGhfMjAxNiA8LSByZWFkX2NzdignaHR0cHM6Ly9yZXN1bHRzLmFlYy5nb3YuYXUvMjA0OTkvV2Vic2l0ZS9Eb3dubG9hZHMvSG91c2VTdGF0ZUZpcnN0UHJlZnNCeVBvbGxpbmdQbGFjZURvd25sb2FkLTIwNDk5LU5TVy5jc3YnLCBza2lwID0gMSkgJT4lIAogIHJiaW5kKHJlYWRfY3N2KCdodHRwczovL3Jlc3VsdHMuYWVjLmdvdi5hdS8yMDQ5OS9XZWJzaXRlL0Rvd25sb2Fkcy9Ib3VzZVN0YXRlRmlyc3RQcmVmc0J5UG9sbGluZ1BsYWNlRG93bmxvYWQtMjA0OTktVklDLmNzdicsIHNraXAgPSAxKSkgJT4lIAogIHJiaW5kKHJlYWRfY3N2KCdodHRwczovL3Jlc3VsdHMuYWVjLmdvdi5hdS8yMDQ5OS9XZWJzaXRlL0Rvd25sb2Fkcy9Ib3VzZVN0YXRlRmlyc3RQcmVmc0J5UG9sbGluZ1BsYWNlRG93bmxvYWQtMjA0OTktUUxELmNzdicsIHNraXAgPSAxKSkgJT4lIAogIHJiaW5kKHJlYWRfY3N2KCdodHRwczovL3Jlc3VsdHMuYWVjLmdvdi5hdS8yMDQ5OS9XZWJzaXRlL0Rvd25sb2Fkcy9Ib3VzZVN0YXRlRmlyc3RQcmVmc0J5UG9sbGluZ1BsYWNlRG93bmxvYWQtMjA0OTktU0EuY3N2Jywgc2tpcCA9IDEpKSAlPiUgCiAgcmJpbmQocmVhZF9jc3YoJ2h0dHBzOi8vcmVzdWx0cy5hZWMuZ292LmF1LzIwNDk5L1dlYnNpdGUvRG93bmxvYWRzL0hvdXNlU3RhdGVGaXJzdFByZWZzQnlQb2xsaW5nUGxhY2VEb3dubG9hZC0yMDQ5OS1XQS5jc3YnLCBza2lwID0gMSkpICU+JSAKICByYmluZChyZWFkX2NzdignaHR0cHM6Ly9yZXN1bHRzLmFlYy5nb3YuYXUvMjA0OTkvV2Vic2l0ZS9Eb3dubG9hZHMvSG91c2VTdGF0ZUZpcnN0UHJlZnNCeVBvbGxpbmdQbGFjZURvd25sb2FkLTIwNDk5LVRBUy5jc3YnLCBza2lwID0gMSkpICU+JSAKICByYmluZChyZWFkX2NzdignaHR0cHM6Ly9yZXN1bHRzLmFlYy5nb3YuYXUvMjA0OTkvV2Vic2l0ZS9Eb3dubG9hZHMvSG91c2VTdGF0ZUZpcnN0UHJlZnNCeVBvbGxpbmdQbGFjZURvd25sb2FkLTIwNDk5LU5ULmNzdicsIHNraXAgPSAxKSkgJT4lIAogIHJiaW5kKHJlYWRfY3N2KCdodHRwczovL3Jlc3VsdHMuYWVjLmdvdi5hdS8yMDQ5OS9XZWJzaXRlL0Rvd25sb2Fkcy9Ib3VzZVN0YXRlRmlyc3RQcmVmc0J5UG9sbGluZ1BsYWNlRG93bmxvYWQtMjA0OTktQUNULmNzdicsIHNraXAgPSAxKSkKYGBgCgpgYGB7cn0KY29hbGl0aW9uX2NvbnRlc3RfMjAxNiA8LSBmcF9ib290aF8yMDE2ICU+JSAKICBncm91cF9ieShEaXZpc2lvbk5tLCBQYXJ0eU5tLCBIaXN0b3JpY0VsZWN0ZWQpICU+JSAKICBzdW1tYXJpc2UoT3JkaW5hcnlWb3RlcyA9IHN1bShPcmRpbmFyeVZvdGVzKSkgJT4lCiAgZmlsdGVyKFBhcnR5Tm0gJWluJSBjKCdMaWJlcmFsJywgJ0NvdW50cnkgTGliZXJhbHMgKE5UKScsCiAgICAgICAgICAgICAgICAgICAgICAgICdMaWJlcmFsIE5hdGlvbmFsIFBhcnR5IG9mIFF1ZWVuc2xhbmQnLAogICAgICAgICAgICAgICAgICAgICAgICAnVGhlIE5hdGlvbmFscycpKSAlPiUgCiAgZ3JvdXBfYnkoRGl2aXNpb25ObSkgJT4lIAogIHRvcF9uKDEpICU+JSAKICBzZWxlY3QoRGl2aXNpb25ObSwgUGFydHlObSkKYGBgCgpJZiB3ZSBsb29rIGF0IGEgY291cGxlIG9mIHRoZSBzdGF0ZXMgd2hlcmUgaGlnaCBpbmNvbWUgYm9vdGhzIHRlbmQgdG8gdm90ZSBzdHJvbmdseSBmb3IgdGhlIGNvYWxpdGlvbiBhcyB3ZWxsIGFzIGxvd2VyIGluY29tZSBib290aHMsIHdlIGNhbiBzZWUgdGhhdCBzb21lIChidXQgbm90IGFsbCkgb2YgdGhlIGxvd2VyIGluY29tZSBib290aHMgYXJlIGNvbnRlc3RlZCBieSBUaGUgTmF0aW9uYWxzLiBUaGlzIGluZGljYXJlcyAobm90IHN1cnByaXNpbmdseSkgdGhhdCBOYXRpb25hbHMgdm90ZXJzIGFuZCBMaWJlcmFsIHZvdGVycyBhcmUgZGlmZmVyZW50IHNvY2lvLWVjb25vbWljYWxseSwgb3IgcG9zc2libHkgdGhhdCBjaXR5IGFuZCBjb3VudHJ5IGNvYWxpdGlvbiB2b3RlcnMgZGlmZmVyLiAKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgbXV0YXRlKERpdmlzaW9uTm0gPSBzdHJpbmdyOjpzdHJfdG9fdGl0bGUoRGl2aXNpb25ObSkpICU+JQogIGlubmVyX2pvaW4oY29hbGl0aW9uX2NvbnRlc3RfMjAxNikgJT4lIAogIGZpbHRlcihTdGF0ZUFiID09ICdOU1cnKSAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gQUxQX1BlcmNlbnQvMTAwLCB4ID0gbWVkaWFuX2hvdXNlaG9sZF9pbmNvbWUsIGNvbG91ciA9IFBhcnR5Tm0pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoJ2JsdWUnLCAnZGFyayBncmVlbicpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpkb2xsYXIpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IEFMUCAyIFBhcnR5IFByZWZlcnJlZCBQZXJjZW50YWdlJywgeCA9ICdCb290aCBNZWRpYW4gSW5jb21lJywKICAgICAgIHkgPSAnQUxQIDJwcCBQZXJjZW50YWdlJywgY29sb3VyID0gJ0NvYWxpdGlvbiBQYXJ0eScsIAogICAgICAgc3VidGl0bGUgPSAnYnkgUG9sbGluZyBCb290aCwgVW53ZWlnaHRlZCAoTlNXKScpIApgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBtdXRhdGUoRGl2aXNpb25ObSA9IHN0cmluZ3I6OnN0cl90b190aXRsZShEaXZpc2lvbk5tKSkgJT4lCiAgaW5uZXJfam9pbihjb2FsaXRpb25fY29udGVzdF8yMDE2KSAlPiUgCiAgZmlsdGVyKFN0YXRlQWIgPT0gJ1ZJQycpICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBBTFBfUGVyY2VudC8xMDAsIHggPSBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgY29sb3VyID0gUGFydHlObSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygnYmx1ZScsICdkYXJrIGdyZWVuJykpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmRvbGxhcikgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB4ID0gJ0Jvb3RoIE1lZGlhbiBJbmNvbWUnLAogICAgICAgeSA9ICdBTFAgMnBwIFBlcmNlbnRhZ2UnLCBjb2xvdXIgPSAnQ29hbGl0aW9uIFBhcnR5JywgCiAgICAgICBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkIChWSUMpJykgCmBgYAoKVGhpcyBlZmZlY3QgaXMgbGVzcyBjbGVhciBpbiBzdGF0ZXMgd2hlcmUgdGhlIE5hdGlvbmFscyBhcmVuJ3QgYXMgcHJvbWluZW50LCBlaXRoZXIgYmVjYXVzZSB0aGUgTmF0aW9uYWxzIGFyZW4ndCBhcyBwcm9taW5lbnQgKFNBLCBXQSwgVEFTKSwgb3IgYXJlIG1lcmdlZCB3aXRoIHRoZSBMaWJlcmFscyAoUUxEKS4gIApgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBtdXRhdGUoRGl2aXNpb25ObSA9IHN0cmluZ3I6OnN0cl90b190aXRsZShEaXZpc2lvbk5tKSkgJT4lCiAgaW5uZXJfam9pbihjb2FsaXRpb25fY29udGVzdF8yMDE2KSAlPiUgCiAgZmlsdGVyKFN0YXRlQWIgPT0gJ1dBJykgJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lLCBjb2xvdXIgPSBQYXJ0eU5tKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCdibHVlJywgJ2RhcmsgZ3JlZW4nKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6ZG9sbGFyKSArCiAgbGFicyh0aXRsZSA9ICcyMDE2IEVsZWN0aW9uOiBBTFAgMiBQYXJ0eSBQcmVmZXJyZWQgUGVyY2VudGFnZScsIHggPSAnQm9vdGggTWVkaWFuIEluY29tZScsCiAgICAgICB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsIGNvbG91ciA9ICdDb2FsaXRpb24gUGFydHknLCAKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQgKFdBKScpIApgYGAKCgpQZXJoYXBzIHdlIHdvdWxkIGJlIGJldHRlciBvZmYgdXNpbmcgdGhlIGdlb2dyYXBoaWNhbCBjbGFzc2lmaWNhdGlvbnMgZnJvbSB0aGUgQUVDLgoKYGBge3J9CmxpYnJhcnkocnZlc3QpCgp3ZWJwYWdlIDwtIHJlYWRfaHRtbCgiaHR0cDovL3Jlc3VsdHMuYWVjLmdvdi5hdS8yMDQ5OS9XZWJzaXRlL0hvdXNlRGl2aXNpb25DbGFzc2lmaWNhdGlvbnMtMjA0OTktTkFULmh0bSIpCgpEaXZpc2lvbl9DbGFzc2lmaWNhdGlvbnMgPC0gd2VicGFnZSAlPiUKICBodG1sX25vZGVzKCIjZGl2aXNpb25DbGFzc2lmaWNhdGlvbnMiKSAlPiUgCiAgaHRtbF90YWJsZShmaWxsID0gVFJVRSkgJT4lCiAgLltbMV1dCkRpdmlzaW9uX0NsYXNzaWZpY2F0aW9ucyA8LSBEaXZpc2lvbl9DbGFzc2lmaWNhdGlvbnMgJT4lIAogIGZpbHRlcihEaXZpc2lvbiAhPSAnVG90YWwgRW5yb2xtZW50JykKCnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuPC0gcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIG11dGF0ZShEaXZpc2lvbiA9IHN0cmluZ3I6OnN0cl90b190aXRsZShEaXZpc2lvbk5tKSkgJT4lCiAgaW5uZXJfam9pbihEaXZpc2lvbl9DbGFzc2lmaWNhdGlvbnMpCmBgYAoKVGhlIGdyYXBoIGJlbG93IHNob3dzIHRoYXQgdGhlIGJvb3RocyB0aGF0IGhhdmUgYSBsb3cgQUxQIDJwcCBhbmQgYSBsb3cgbWVkaWFuIGluY29tZSBhcmUgcHJpbWFyaWx5IHJ1cmFsIGJvb3Rocy4gVGhpcyByZWxhdGlvbnNoaXAgc2VlbXMgc3Ryb25nZXIgdGhhbiB0aGUgTGliL05hdCBzcGxpdC4KYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZmlsdGVyKFN0YXRlQWIgPT0gJ05TVycpICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBBTFBfUGVyY2VudC8xMDAsIHggPSBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgY29sb3VyID0gRGVtb2dyYXBoaWMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpkb2xsYXIpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IEFMUCAyIFBhcnR5IFByZWZlcnJlZCBQZXJjZW50YWdlJywgCiAgICAgICB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsCiAgICAgICB4ID0gJ0Jvb3RoIE1lZGlhbiBJbmNvbWUnLCBjb2xvdXIgPSAnUmVnaW9uJywKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQgKE5TVyknKQpgYGAKCkxvb2tpbmcgYXQgYWxsIHN0YXRlcyB3ZSBzZWUgYSBzaW1pbGFyIHJlbGF0aW9uc2hpcCwgYWx0aG91Z2ggbGVzcyBzdHJvbmcgdGhhbiBpbiBOU1cuCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lLCBjb2xvdXIgPSBEZW1vZ3JhcGhpYykpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmRvbGxhcikgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsCiAgICAgICB4ID0gJ01lZGlhbiBCb290aCBJbmNvbWUnLCBjb2xvdXIgPSAnUmVnaW9uJywKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCldoYXQgYWJvdXQgc29tZSBvZiB0aGUgb3RoZXIgdmFyaWFibGVzPwoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gQUxQX1BlcmNlbnQvMTAwLCB4ID0gYXZlcmFnZV9ob3VzZWhvbGRfc2l6ZSwgY29sb3VyID0gRGVtb2dyYXBoaWMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsCiAgICAgICB4ID0gJ0F2ZXJhZ2UgSG91c2Vob2xkIFNpemUnLCBjb2xvdXIgPSAnUmVnaW9uJywKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IHBlcmNlbnRfZmVtYWxlLCBjb2xvdXIgPSBEZW1vZ3JhcGhpYykpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IEFMUCAyIFBhcnR5IFByZWZlcnJlZCBQZXJjZW50YWdlJywgeSA9ICdBTFAgMnBwIFBlcmNlbnRhZ2UnLAogICAgICAgeCA9ICdQZXJjZW50IEZlbWFsZScsIGNvbG91ciA9ICdSZWdpb24nLAogICAgICAgc3VidGl0bGUgPSAnYnkgUG9sbGluZyBCb290aCwgVW53ZWlnaHRlZCcpCmBgYAoKYGBge3J9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBzZWxlY3QoQUxQX1BlcmNlbnQsIFN3aW5nLCBtZWRpYW5fYWdlLCBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgYXZlcmFnZV9ob3VzZWhvbGRfc2l6ZSwKICAgICAgICAgcGVyc29uc19wZXJfYmVkcm9vbSwgbWVkaWFuX3dlZWtseV9yZW50LCBtZWRpYW5fYW5udWFsX21vcnRnYWdlLAogICAgICAgICBwZXJjZW50X2ZlbWFsZSwgcGVyY2VudF9kZWZhY3RvLCBwZXJjZW50X2Jvcm5faW5fYXVzdHJhbGlhLAogICAgICAgICBwZXJjZW50X3VuaXQsIHBlcmNlbnRfbW9ydGdhZ2UsIHBlcmNlbnRfcmVudCkgJT4lIAogIGNvciAlPiUgCiAga2FibGUoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IHBlcnNvbnNfcGVyX2JlZHJvb20sIGNvbG91ciA9IERlbW9ncmFwaGljKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IEFMUCAyIFBhcnR5IFByZWZlcnJlZCBQZXJjZW50YWdlJywgeSA9ICdBTFAgMnBwIFBlcmNlbnRhZ2UnLAogICAgICAgeCA9ICdQZXJzb25zIHBlciBCZWRyb29tJywgY29sb3VyID0gJ1JlZ2lvbicsCiAgICAgICBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkJykKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9NCwgZXJyb3I9RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBnZ3Bsb3QoYWVzKHkgPSBBTFBfUGVyY2VudC8xMDAsIHggPSBwZXJjZW50X3VuaXQsIGNvbG91ciA9IERlbW9ncmFwaGljKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsCiAgICAgICB4ID0gJ1BlcmNlbnQgb2YgRHdlbGxpbmdzIC0gVW5pdCcsIGNvbG91ciA9ICdSZWdpb24nLAogICAgICAgc3VidGl0bGUgPSAnYnkgUG9sbGluZyBCb290aCwgVW53ZWlnaHRlZCcpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gQUxQX1BlcmNlbnQvMTAwLCB4ID0gcGVyY2VudF9tb3J0Z2FnZSwgY29sb3VyID0gRGVtb2dyYXBoaWMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgbGFicyh0aXRsZSA9ICcyMDE2IEVsZWN0aW9uOiBBTFAgMiBQYXJ0eSBQcmVmZXJyZWQgUGVyY2VudGFnZScsIHkgPSAnQUxQIDJwcCBQZXJjZW50YWdlJywKICAgICAgIHggPSAnUGVyY2VudCBvZiBEd2VsbGluZ3MgLSBVbmRlciBNb3J0Z2FnZScsIGNvbG91ciA9ICdSZWdpb24nLAogICAgICAgc3VidGl0bGUgPSAnYnkgUG9sbGluZyBCb290aCwgVW53ZWlnaHRlZCcpCmBgYAoKYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTQsIGVycm9yPUYsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gQUxQX1BlcmNlbnQvMTAwLCB4ID0gcGVyY2VudF9yZW50LCBjb2xvdXIgPSBEZW1vZ3JhcGhpYykpICsKICBnZW9tX3BvaW50KHNpemUgPSAxKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNikgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBsYWJzKHRpdGxlID0gJzIwMTYgRWxlY3Rpb246IEFMUCAyIFBhcnR5IFByZWZlcnJlZCBQZXJjZW50YWdlJywgeSA9ICdBTFAgMnBwIFBlcmNlbnRhZ2UnLAogICAgICAgeCA9ICdQZXJjZW50IG9mIER3ZWxsaW5ncyAtIFJlbnRpbmcnLCBjb2xvdXIgPSAnUmVnaW9uJywKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IHBlcmNlbnRfaW5kaWcsIGNvbG91ciA9IERlbW9ncmFwaGljKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsCiAgICAgICB4ID0gJ1BlcmNlbnQgSW5kaWdlbmVvdXMnLCBjb2xvdXIgPSAnUmVnaW9uJywKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IHBlcmNlbnRfYm9ybl9pbl9hdXN0cmFsaWEsIGNvbG91ciA9IERlbW9ncmFwaGljKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEpICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIGxhYnModGl0bGUgPSAnMjAxNiBFbGVjdGlvbjogQUxQIDIgUGFydHkgUHJlZmVycmVkIFBlcmNlbnRhZ2UnLCB5ID0gJ0FMUCAycHAgUGVyY2VudGFnZScsCiAgICAgICB4ID0gJ1BlcmNlbnQgQm9ybiBpbiBBdXN0cmFsaWEnLCBjb2xvdXIgPSAnUmVnaW9uJywKICAgICAgIHN1YnRpdGxlID0gJ2J5IFBvbGxpbmcgQm9vdGgsIFVud2VpZ2h0ZWQnKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD00LCBlcnJvcj1GLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4gJT4lIAogIGdncGxvdChhZXMoeSA9IEFMUF9QZXJjZW50LzEwMCwgeCA9IHBlcmNlbnRfZGVmYWN0bywgY29sb3VyID0gRGVtb2dyYXBoaWMpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiRGFyazIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArCiAgbGFicyh0aXRsZSA9ICcyMDE2IEVsZWN0aW9uOiBBTFAgMiBQYXJ0eSBQcmVmZXJyZWQgUGVyY2VudGFnZScsIHkgPSAnQUxQIDJwcCBQZXJjZW50YWdlJywKICAgICAgIHggPSAnUGVyY2VudCBpbiBhIERlZmFjdG8gUmVsYXRpb25zaGlwJywgY29sb3VyID0gJ1JlZ2lvbicsCiAgICAgICBzdWJ0aXRsZSA9ICdieSBQb2xsaW5nIEJvb3RoLCBVbndlaWdodGVkJykKYGBgCgojIFdoYXQgdG8gZG8gbmV4dD8KCkFkZCBleHRyYSB2YXJpYWJsZXMKTG9vayBhdCBsYWdnZWQgcmVzdWx0cwpCdWlsZCBtb2RlbHMKCiMgU2ltcGxlIE1vZGVsIEJ1aWxkaW5nCgojIyBMaW5lYXIgTW9kZWxzCgpDYW4gd2UgYnVpbGQgYSBzaW1wbGUgbGluZWFyIG1vZGVsIHRvIHByZWRpY3QgMnBwCgpgYGB7cn0KbGlicmFyeShNQVNTKQpsaWJyYXJ5KGNhcikKYWxwXzJwcF9sbV9kZW1vZyA8LSAKICBsbShBTFBfUGVyY2VudCB+IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lKkRlbW9ncmFwaGljICsgcGVyY2VudF9pbmRpZyArIAogICAgICAgcGVyY2VudF9mZW1hbGUgKyBwZXJjZW50X2RlZmFjdG8gKyBwZXJjZW50X2Jvcm5faW5fYXVzdHJhbGlhICsgCiAgICAgICBwZXJjZW50X3JlbnQgKyBtZWRpYW5fd2Vla2x5X3JlbnQgKyBtZWRpYW5fYWdlLCAKICAgICBkYXRhID0gcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4pCgpzdW1tYXJ5KGFscF8ycHBfbG1fZGVtb2cpCmFub3ZhKGFscF8ycHBfbG1fZGVtb2cpCnBsb3QoYWxwXzJwcF9sbV9kZW1vZykKdmlmKGFscF8ycHBfbG1fZGVtb2cpCiMgY3JQbG90cyhhbHBfMnBwX2xtX2RlbW9nKQojIGNlcmVzUGxvdHMoYWxwXzJwcF9sbV9kZW1vZykKCmxpYnJhcnkoZ3ZsbWEpCmd2X2FscF8ycHBfbG1fZGVtb2cgPC0gZ3ZsbWEoYWxwXzJwcF9sbV9kZW1vZykKc3VtbWFyeShndl9hbHBfMnBwX2xtX2RlbW9nKQpgYGAKCgpgYGB7cn0KbGlicmFyeShNQVNTKQpsaWJyYXJ5KGNhcikKYWxwXzJwcF9sbV9kZW1vZyA8LSAKICBsbShBTFBfUGVyY2VudCB+IG1lZGlhbl9ob3VzZWhvbGRfaW5jb21lICsgRGVtb2dyYXBoaWMgKyBwZXJjZW50X2luZGlnICsgCiAgICAgICBwZXJjZW50X2RlZmFjdG8gKyBwZXJjZW50X2Jvcm5faW5fYXVzdHJhbGlhICsgCiAgICAgICBwZXJjZW50X3JlbnQgKyBtZWRpYW5fd2Vla2x5X3JlbnQgKyBtZWRpYW5fYWdlLCAKICAgICBkYXRhID0gcG9sbGluZ19wbGFjZV8ycHBfY2xlYW4pCgpzdW1tYXJ5KGFscF8ycHBfbG1fZGVtb2cpCmFub3ZhKGFscF8ycHBfbG1fZGVtb2cpCnBsb3QoYWxwXzJwcF9sbV9kZW1vZykKdmlmKGFscF8ycHBfbG1fZGVtb2cpCmNyUGxvdHMoYWxwXzJwcF9sbV9kZW1vZykKY2VyZXNQbG90cyhhbHBfMnBwX2xtX2RlbW9nKQoKbGlicmFyeShndmxtYSkKZ3ZfYWxwXzJwcF9sbV9kZW1vZyA8LSBndmxtYShhbHBfMnBwX2xtX2RlbW9nKQpzdW1tYXJ5KGd2X2FscF8ycHBfbG1fZGVtb2cpCmBgYAoKV2Uga25vdyB0aGF0IGluY29tZSBpcyBpbXBvcnRhbnQgaW4gdGhlIG5vbi1ydXJhbCBhcmVhcywgc28gaXQgbWlnaHQgYmUgd29ydGggYWRkaW5nIGFuIGludGVyYWN0aW9uIGJldHdlZW4gcnVyYWwgYW5kIG5vbi1ydXJhbCBhbmQgaW5jb21lLgoKYGBge3J9CnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuIDwtIHBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuICU+JSAKICBtdXRhdGUoTm9uUnVyYWxfRGVtb2dyYXBoaWMgPSBpZl9lbHNlKERlbW9ncmFwaGljID09ICdSdXJhbCcsIDEsIDApLAogICAgICAgICBBTFBfUGVyY2VudF8yMDEzID0gQUxQX1BlcmNlbnQgKyBTd2luZykKCmFscF8ycHBfbG1fZGVtb2cgPC0gCiAgbG0oQUxQX1BlcmNlbnQgfiBtZWRpYW5faG91c2Vob2xkX2luY29tZSArIERlbW9ncmFwaGljICsgcGVyY2VudF9pbmRpZyArIAogICAgICAgcGVyY2VudF9kZWZhY3RvICsgcGVyY2VudF9ib3JuX2luX2F1c3RyYWxpYSArIAogICAgICAgcGVyY2VudF9yZW50ICsgbWVkaWFuX3dlZWtseV9yZW50ICsgbWVkaWFuX2FnZSArCiAgICAgICBOb25SdXJhbF9EZW1vZ3JhcGhpYzptZWRpYW5faG91c2Vob2xkX2luY29tZSwgCiAgICAgZGF0YSA9IHBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuKQoKc3VtbWFyeShhbHBfMnBwX2xtX2RlbW9nKQphbm92YShhbHBfMnBwX2xtX2RlbW9nKQpwbG90KGFscF8ycHBfbG1fZGVtb2cpCnZpZihhbHBfMnBwX2xtX2RlbW9nKQojIGNyUGxvdHMoYWxwXzJwcF9sbV9kZW1vZykKIyBjZXJlc1Bsb3RzKGFscF8ycHBfbG1fZGVtb2cpCgpndl9hbHBfMnBwX2xtX2RlbW9nIDwtIGd2bG1hKGFscF8ycHBfbG1fZGVtb2cpCnN1bW1hcnkoZ3ZfYWxwXzJwcF9sbV9kZW1vZykKYGBgCgoKIyMgTGFzc28gUmVncmVzc2lvbgoKVXNpbmcgYSBsYXNzbyByZWdyZXNzaW9uLCAKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGdsbW5ldCkKCnBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuX05BIDwtIG5hLm9taXQocG9sbGluZ19wbGFjZV8ycHBfY2xlYW4pICU+JSAKICBkcGx5cjo6c2VsZWN0KEFMUF9QZXJjZW50LCBTdGF0ZUFiLCBtZWRpYW5fYWdlLCBtZWRpYW5faG91c2Vob2xkX2luY29tZSwgCiAgICAgICAgICAgICAgICBhdmVyYWdlX2hvdXNlaG9sZF9zaXplLCBwZXJzb25zX3Blcl9iZWRyb29tLCBtZWRpYW5fd2Vla2x5X3JlbnQsCiAgICAgICAgICAgICAgICBtZWRpYW5fYW5udWFsX21vcnRnYWdlLCAgcGVyY2VudF9mZW1hbGUsIHBlcmNlbnRfZGVmYWN0bywgCiAgICAgICAgICAgICAgICBwZXJjZW50X21hcnJpZWQsIHBlcmNlbnRfaW5kaWcsIHBlcmNlbnRfYm9ybl9pbl9hdXN0cmFsaWEsIAogICAgICAgICAgICAgICAgcGVyY2VudF91bml0LCBwZXJjZW50X21vcnRnYWdlLCBwZXJjZW50X3JlbnQsIERlbW9ncmFwaGljKQojIEluc3BlY3QgdGhlIGRhdGEKc2FtcGxlX24ocG9sbGluZ19wbGFjZV8ycHBfY2xlYW5fTkEsIDMpCiMgU3BsaXQgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdCBzZXQKc2V0LnNlZWQoMTIzKQp0cmFpbmluZy5zYW1wbGVzIDwtIHBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuX05BJEFMUF9QZXJjZW50ICU+JSAKICBjcmVhdGVEYXRhUGFydGl0aW9uKHAgPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW4uZGF0YSAgPC0gcG9sbGluZ19wbGFjZV8ycHBfY2xlYW5fTkFbdHJhaW5pbmcuc2FtcGxlcywgXQp0ZXN0LmRhdGEgPC0gcG9sbGluZ19wbGFjZV8ycHBfY2xlYW5fTkFbLXRyYWluaW5nLnNhbXBsZXMsIF0KCiMgRHVteSBjb2RlIGNhdGVnb3JpY2FsIHByZWRpY3RvciB2YXJpYWJsZXMKeCA8LSBtb2RlbC5tYXRyaXgoQUxQX1BlcmNlbnR+LiwgdHJhaW4uZGF0YSlbLC0xXQojIENvbnZlcnQgdGhlIG91dGNvbWUgKGNsYXNzKSB0byBhIG51bWVyaWNhbCB2YXJpYWJsZQp5IDwtIHRyYWluLmRhdGEkQUxQX1BlcmNlbnQKCmN2Lmxhc3NvIDwtIGN2LmdsbW5ldCh4LCB5LCBhbHBoYSA9IDEsIGZhbWlseSA9ICJnYXVzc2lhbiIpCgojIEZpdCB0aGUgZmluYWwgbW9kZWwgb24gdGhlIHRyYWluaW5nIGRhdGEKYWxwXzJwcF9sYXNzb19kZW1vZyA8LSBnbG1uZXQoeCwgeSwgYWxwaGEgPSAxLCBmYW1pbHkgPSAiZ2F1c3NpYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYW1iZGEgPSBjdi5sYXNzbyRsYW1iZGEubWluKQojIERpc3BsYXkgcmVncmVzc2lvbiBjb2VmZmljaWVudHMKY29lZihhbHBfMnBwX2xhc3NvX2RlbW9nKQoKc3VtbWFyeShhbHBfMnBwX2xhc3NvX2RlbW9nKQoKYWxwXzJwcF9sYXNzb19kZW1vZyRkZXYucmF0aW8KCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBkYXRhCngudGVzdCA8LSBtb2RlbC5tYXRyaXgoQUxQX1BlcmNlbnQgfi4sIHRlc3QuZGF0YSlbLC0xXQpwcmVkaWN0aW9ucyA8LSBhbHBfMnBwX2xhc3NvX2RlbW9nICU+JSBwcmVkaWN0KG5ld3ggPSB4LnRlc3QpCiMgTW9kZWwgYWNjdXJhY3kKb2JzZXJ2ZWQgPC0gdGVzdC5kYXRhJEFMUF9QZXJjZW50CgpwbG90KHByZWRpY3Rpb25zLCBvYnNlcnZlZCkKY29yKHByZWRpY3Rpb25zLCBvYnNlcnZlZCkKCmNvcihwcmVkaWN0aW9ucywgb2JzZXJ2ZWQpXjIKYGBgCgoKV2hhdCBpZiB3ZSBpbmNsdWRlIHRoZSAycHAgZnJvbSB0aGUgbGFzdCBlbGVjdGlvbi4gSXQgYXBwZWFycyB0aGF0IFN3aW5nIGlzIGRlZmluZWQgYXMgdGhlIHN3aW5nIHRvIHRoZSBDb2FsaXRpb24gYmV0d2VlbiAyMDEzIGFuZCAyMDE2LiBTbyBpZiB3ZSBhZGQgdGhlIHN3aW5nIHRvIHRoZSAyMDE2IDJwcCwgdGhlbiB3ZSBvYnRhaW4gdGhlIDIwMTMgMnBwLgoKV2hlbiB1c2luZyBhIExpbmVhciBNb2RlbCwgd2UgY2FuIGdldCBhbiBSXjIgb2YgYWJvdXQgODQlCmBgYHtyfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiA8LSBwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbiAlPiUgCiAgbXV0YXRlKGxvZ2l0X0FMUF9QZXJjZW50ID0gbG9nKChBTFBfUGVyY2VudC8xMDApLygxLUFMUF9QZXJjZW50LzEwMCkpLAogICAgICAgICBsb2dpdF9BTFBfUGVyY2VudF8yMDEzID0gbG9nKChBTFBfUGVyY2VudF8yMDEzLzEwMCkvKDEtQUxQX1BlcmNlbnRfMjAxMy8xMDApKSkgJT4lIAogIGZpbHRlcighaXMubmFuKGxvZ2l0X0FMUF9QZXJjZW50KSkgJT4lIAogIGZpbHRlcighaXMubmEobG9naXRfQUxQX1BlcmNlbnQpKSAlPiUgCiAgZmlsdGVyKCFpcy5pbmZpbml0ZShsb2dpdF9BTFBfUGVyY2VudCkpICU+JSAKICBmaWx0ZXIoIWlzLm5hbihsb2dpdF9BTFBfUGVyY2VudF8yMDEzKSkgJT4lIAogIGZpbHRlcighaXMubmEobG9naXRfQUxQX1BlcmNlbnRfMjAxMykpICU+JSAKICBmaWx0ZXIoIWlzLmluZmluaXRlKGxvZ2l0X0FMUF9QZXJjZW50XzIwMTMpKQoKYWxwXzJwcF9sbV9kZW1vZ19sYWcgPC0gCiAgbG0obG9naXRfQUxQX1BlcmNlbnQgfiBwZXJjZW50X2luZGlnICsgcGVyY2VudF9kZWZhY3RvICsKICAgICAgIHBlcmNlbnRfcmVudCArIG1lZGlhbl93ZWVrbHlfcmVudCArIG1lZGlhbl9hZ2UgKyAKICAgICAgIE5vblJ1cmFsX0RlbW9ncmFwaGljKm1lZGlhbl9ob3VzZWhvbGRfaW5jb21lICsgbG9naXRfQUxQX1BlcmNlbnRfMjAxMywgCiAgICAgZGF0YSA9IHBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuKQoKc3VtbWFyeShhbHBfMnBwX2xtX2RlbW9nX2xhZykKYW5vdmEoYWxwXzJwcF9sbV9kZW1vZ19sYWcpCnBsb3QoYWxwXzJwcF9sbV9kZW1vZ19sYWcpCnZpZihhbHBfMnBwX2xtX2RlbW9nX2xhZykKIyBjclBsb3RzKGFscF8ycHBfbG1fZGVtb2dfbGFnKQojIGNlcmVzUGxvdHMoYWxwXzJwcF9sbV9kZW1vZ19sYWcpCgpndl9hbHBfMnBwX2xtX2RlbW9nX2xhZyA8LSBndmxtYShhbHBfMnBwX2xtX2RlbW9nX2xhZykKc3VtbWFyeShhbHBfMnBwX2xtX2RlbW9nX2xhZykKYGBgCgpJdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBzZWUgd2hldGhlciAyMDEzIGFsb25lIGlzIGEgZ29vZCBwcmVkaWN0b3IuIEZpdHRpbmcgdGhpcyBtb2RlbCBnaXZlcyBhbiBSXjIgb2YgODIuNyUuIFRoaXMgbWVhbnMgdGhhdCB0aGUgb3RoZXIgdmFyaWFibGVzIGFkZCBhIGJpdCwgYnV0IG5vdCBhIGh1Z2UgYW1vdW50LiAKYGBge3J9CmFscF8ycHBfbG1fZGVtb2dfbGFnIDwtIAogIGxtKGxvZ2l0X0FMUF9QZXJjZW50IH4gbG9naXRfQUxQX1BlcmNlbnRfMjAxMywgCiAgICAgZGF0YSA9IHBvbGxpbmdfcGxhY2VfMnBwX2NsZWFuKQoKc3VtbWFyeShhbHBfMnBwX2xtX2RlbW9nX2xhZykKYW5vdmEoYWxwXzJwcF9sbV9kZW1vZ19sYWcpCnBsb3QoYWxwXzJwcF9sbV9kZW1vZ19sYWcpCmBgYAoKVGhlcmUgYXJlIHRocmVlIG91dGx5aW5nIHZhbHVlcyAtIGxldCdzIGV4cGxvcmUgdGhlc2UKCmBgYHtyfQpwb2xsaW5nX3BsYWNlXzJwcF9jbGVhbltjKDIwNzksIDcyNjcsIDI3NTgpLF0KYGBgCgpUaGVzZSBib290aHMgaGF2ZSBmZXdlciB0aGFuIDQwIGVsZWN0b3JzLCBhbmQgZmV3ZXIgdGhhbiA1IGZvciBvbmUgb2YgdGhlIHR3byBwYXJ0aWVzLiBUaGV5IGFyZSBhbHNvICdub24tc3RhbmRhcmQnIGJvb3Rocy4KCkl0IG1pZ2h0IGJlIGludGVyZXN0aW5nIHRvIHNlZSB3aGljaCBib290aHMgZGV2aWF0ZSBmcm9tIHRoZSBkaXZpc2lvbiBtZWFuLgoKCgoKCgoKCgoKCgo=